diff --git a/README.md b/README.md index a9f4eb4ae..40def5d90 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ # PDI SDK Samples +fork官方demo,编写通用dialog +操作界面统一使用一个JSON对象作为参数设置途径。 +采用JSON作为参数配置,省去ui调试设计步骤,只需一个类就可以开发一个插件。 + ### Pre-requisites for building the project: * Maven, version 3+ diff --git a/kettle-sdk-embedding-samples/pom.xml b/kettle-sdk-embedding-samples/pom.xml index a094b14c4..3a9abf70c 100644 --- a/kettle-sdk-embedding-samples/pom.xml +++ b/kettle-sdk-embedding-samples/pom.xml @@ -28,12 +28,26 @@ commons-httpclient commons-httpclient + + commons-codec + commons-codec + pentaho-kettle kettle-engine ${pdi.version} + + + commons-dbcp + commons-dbcp + + + commons-codec + commons-codec + + org.pentaho.di.plugins @@ -105,5 +119,27 @@ 3.0.1 test + + + + + com.alibaba + fastjson + 1.2.47 + + + + commons-net + commons-net + 3.6 + + + + org.projectlombok + lombok + 1.18.2 + provided + + diff --git a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpJobs.java b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpJobs.java new file mode 100644 index 000000000..fbac41c7a --- /dev/null +++ b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpJobs.java @@ -0,0 +1,188 @@ +package org.pentaho.di.sdk.samples.embedding; + +import java.io.File; + +import org.apache.commons.io.FileUtils; +import org.pentaho.di.core.KettleEnvironment; +import org.pentaho.di.core.logging.LogLevel; +import org.pentaho.di.job.JobHopMeta; +import org.pentaho.di.job.JobMeta; +import org.pentaho.di.job.entries.ftp.JobEntryFTP; +import org.pentaho.di.job.entries.special.JobEntrySpecial; +import org.pentaho.di.job.entries.success.JobEntrySuccess; +import org.pentaho.di.job.entries.writetolog.JobEntryWriteToLog; +import org.pentaho.di.job.entry.JobEntryCopy; + +/** + * This class demonstrates how to create a PDI FTP job definition + * in code, and save it to a kjb file. + */ +public class GeneratingFtpJobs { + + public static GeneratingFtpJobs instance; + + /** + * @param args not used + */ + public static void main( String[] args ) { + + try { + // Kettle Environment must be initialized first when using PDI + // It bootstraps the PDI engine by loading settings, appropriate plugins + // etc. + KettleEnvironment.init( false ); + + // Create an instance of this demo class for convenience + instance = new GeneratingFtpJobs(); + + // generates a simple job, returning the JobMeta object describing it + JobMeta jobMeta = instance.generateJob(); + + // get the xml of the definition and save it to a file for inspection in spoon + String outputFilename = "etl/generated_ftp_job.kjb"; + System.out.println( "- Saving to " + outputFilename ); + String xml = jobMeta.getXML(); + File file = new File( outputFilename ); + FileUtils.writeStringToFile( file, xml, "UTF-8" ); + + System.out.println( "DONE" ); + } catch ( Exception e ) { + e.printStackTrace(); + return; + } + + } + + /** + * This method generates a job definition from scratch. + * + * It demonstrates the following: + * + * - Creating a new job + * - Creating and connecting job entries + * + * @return the generated job definition + */ + public JobMeta generateJob() { + + try { + System.out.println( "Generating a FTP job definition" ); + + // create empty transformation definition + JobMeta jobMeta = new JobMeta(); + jobMeta.setName( "Generated Demo FTP Job" ); + + // ------------------------------------------------------------------------------------ + // Create start entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Start Entry" ); + + // Create and configure start entry + JobEntrySpecial start = new JobEntrySpecial(); + start.setName( "START" ); + start.setStart( true ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy startEntry = new JobEntryCopy( start ); + + // place it on Spoon canvas properly + startEntry.setDrawn( true ); + startEntry.setLocation( 100, 100 ); + + jobMeta.addJobEntry( startEntry ); + + // ------------------------------------------------------------------------------------ + // Create "write to log" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Write To Log Entry" ); + + // Create and configure entry + JobEntryWriteToLog writeToLog = new JobEntryWriteToLog(); + writeToLog.setName( "This is FTP job example" ); + writeToLog.setLogLevel( LogLevel.MINIMAL ); + writeToLog.setLogSubject( "Logging PDI Build Information:" ); + writeToLog.setLogMessage( "Version: ${Internal.Kettle.Version}\n" + + "Build Date: ${Internal.Kettle.Build.Date}" ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy writeToLogEntry = new JobEntryCopy( writeToLog ); + + // place it on Spoon canvas properly + writeToLogEntry.setDrawn( true ); + writeToLogEntry.setLocation( 300, 100 ); + + jobMeta.addJobEntry( writeToLogEntry ); + + // connect start entry to logging entry using simple hop + jobMeta.addJobHop( new JobHopMeta( startEntry, writeToLogEntry ) ); + + + // ------------------------------------------------------------------------------------ + // Create "FTP" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding FTP Entry" ); + + // crate and configure entry + // + JobEntryFTP ftp = new JobEntryFTP(); + ftp.setName( "FTP Job" ); + //set ftp parameters + ftp.setServerName("11.12.112.84"); + ftp.setPort("21"); + ftp.setUserName("ftp1"); + // + ftp.setFtpDirectory("/"); + ftp.setWildcard(".*"); + ftp.setTargetDirectory("/tmp/"); + + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy ftpEntry = new JobEntryCopy( ftp ); + + // place it on Spoon canvas properly + ftpEntry.setDrawn( true ); + ftpEntry.setLocation( 500, 100 ); + + jobMeta.addJobEntry( ftpEntry ); + + // connect logging entry to FTP entry on true evaluation + JobHopMeta greenHop1 = new JobHopMeta( writeToLogEntry, ftpEntry ); + greenHop1.setEvaluation( true ); + jobMeta.addJobHop( greenHop1 ); + + + // ------------------------------------------------------------------------------------ + // Create "success" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Success Entry" ); + + // crate and configure entry + JobEntrySuccess success = new JobEntrySuccess(); + success.setName( "Success" ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy successEntry = new JobEntryCopy( success ); + + // place it on Spoon canvas properly + successEntry.setDrawn( true ); + successEntry.setLocation( 700, 100 ); + + jobMeta.addJobEntry( successEntry ); + + // connect logging entry to success entry on TRUE evaluation + JobHopMeta greenHop = new JobHopMeta( ftpEntry, successEntry ); + greenHop.setEvaluation( true ); + jobMeta.addJobHop( greenHop ); + + + + return jobMeta; + + } catch ( Exception e ) { + + // something went wrong, just log and return + e.printStackTrace(); + return null; + } + } +} diff --git a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpPlusJobs.java b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpPlusJobs.java new file mode 100644 index 000000000..cce3ebce7 --- /dev/null +++ b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/GeneratingFtpPlusJobs.java @@ -0,0 +1,194 @@ +package org.pentaho.di.sdk.samples.embedding; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.io.FileUtils; +import org.pentaho.di.core.KettleEnvironment; +import org.pentaho.di.core.logging.LogLevel; +import org.pentaho.di.job.JobHopMeta; +import org.pentaho.di.job.JobMeta; +import org.pentaho.di.job.entries.special.JobEntrySpecial; +import org.pentaho.di.job.entries.success.JobEntrySuccess; +import org.pentaho.di.job.entries.writetolog.JobEntryWriteToLog; +import org.pentaho.di.job.entry.JobEntryCopy; +import org.pentaho.di.sdk.samples.embedding.entries.ftpplus.JobEntryFtpPlus; +import org.pentaho.di.sdk.samples.embedding.entries.ftpplus.JobEntryFtpPlusParamsDO; + +import java.io.File; + +public class GeneratingFtpPlusJobs { + + public static GeneratingFtpPlusJobs instance; + + /** + * @param args not used + */ + public static void main( String[] args ) { + + try { + // Kettle Environment must be initialized first when using PDI + // It bootstraps the PDI engine by loading settings, appropriate plugins + // etc. + KettleEnvironment.init( false ); + + // Create an instance of this demo class for convenience + instance = new GeneratingFtpPlusJobs(); + + // generates a simple job, returning the JobMeta object describing it + JobMeta jobMeta = instance.generateJob(); + + // get the xml of the definition and save it to a file for inspection in spoon + String outputFilename = "etl/generated_ftp_job.kjb"; + System.out.println( "- Saving to " + outputFilename ); + String xml = jobMeta.getXML(); + File file = new File( outputFilename ); + FileUtils.writeStringToFile( file, xml, "UTF-8" ); + + System.out.println( "DONE" ); + } catch ( Exception e ) { + e.printStackTrace(); + return; + } + + } + + /** + * This method generates a job definition from scratch. + * + * It demonstrates the following: + * + * - Creating a new job + * - Creating and connecting job entries + * + * @return the generated job definition + */ + public JobMeta generateJob() { + + try { + System.out.println( "Generating a FTP job definition" ); + + // create empty transformation definition + JobMeta jobMeta = new JobMeta(); + jobMeta.setName( "Generated Demo FtpPlus Job" ); + + // ------------------------------------------------------------------------------------ + // Create start entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Start Entry" ); + + // Create and configure start entry + JobEntrySpecial start = new JobEntrySpecial(); + start.setName( "START" ); + start.setStart( true ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy startEntry = new JobEntryCopy( start ); + + // place it on Spoon canvas properly + startEntry.setDrawn( true ); + startEntry.setLocation( 100, 100 ); + + jobMeta.addJobEntry( startEntry ); + + // ------------------------------------------------------------------------------------ + // Create "write to log" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Write To Log Entry" ); + + // Create and configure entry + JobEntryWriteToLog writeToLog = new JobEntryWriteToLog(); + writeToLog.setName( "This is FTP job example" ); + writeToLog.setLogLevel( LogLevel.MINIMAL ); + writeToLog.setLogSubject( "Logging PDI Build Information:" ); + writeToLog.setLogMessage( "Version: ${Internal.Kettle.Version}\n" + + "Build Date: ${Internal.Kettle.Build.Date}" ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy writeToLogEntry = new JobEntryCopy( writeToLog ); + + // place it on Spoon canvas properly + writeToLogEntry.setDrawn( true ); + writeToLogEntry.setLocation( 300, 100 ); + + jobMeta.addJobEntry( writeToLogEntry ); + + // connect start entry to logging entry using simple hop + jobMeta.addJobHop( new JobHopMeta( startEntry, writeToLogEntry ) ); + + + // ------------------------------------------------------------------------------------ + // Create "FTP" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding FTP Entry" ); + + // crate and configure entry + // + JobEntryFtpPlus ftp = new JobEntryFtpPlus(); + JobEntryFtpPlusParamsDO jobEntryFtpPlusParamsDO = new JobEntryFtpPlusParamsDO(); + jobEntryFtpPlusParamsDO.setServerName("127.0.0.1"); + jobEntryFtpPlusParamsDO.setPort("21"); + jobEntryFtpPlusParamsDO.setUserName("ftp1"); + jobEntryFtpPlusParamsDO.setPassword("ftp1"); + jobEntryFtpPlusParamsDO.setFtpDirectory("/"); + jobEntryFtpPlusParamsDO.setWildcard(".*"); + jobEntryFtpPlusParamsDO.setTargetDirectory("/tmp"); + + ftp.setName("FtpPlus"); + ftp.setPluginId("JobEntryFtpPlus"); + String jsonString = JSON.toJSONString(jobEntryFtpPlusParamsDO); + //String jsonString = JSONObject.toJSONString(jobEntryFtpPlusParamsDO); + ftp.setJsonConfStr(jsonString); + + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy ftpEntry = new JobEntryCopy( ftp ); + + // place it on Spoon canvas properly + ftpEntry.setDrawn( true ); + ftpEntry.setLocation( 500, 100 ); + + jobMeta.addJobEntry( ftpEntry ); + + // connect logging entry to FTP entry on true evaluation + JobHopMeta greenHop1 = new JobHopMeta( writeToLogEntry, ftpEntry ); + greenHop1.setEvaluation( true ); + jobMeta.addJobHop( greenHop1 ); + + + // ------------------------------------------------------------------------------------ + // Create "success" entry and put it into the job + // ------------------------------------------------------------------------------------ + System.out.println( "- Adding Success Entry" ); + + // crate and configure entry + JobEntrySuccess success = new JobEntrySuccess(); + success.setName( "Success" ); + + // wrap into JobEntryCopy object, which holds generic job entry information + JobEntryCopy successEntry = new JobEntryCopy( success ); + + // place it on Spoon canvas properly + successEntry.setDrawn( true ); + successEntry.setLocation( 700, 100 ); + + jobMeta.addJobEntry( successEntry ); + + // connect logging entry to success entry on TRUE evaluation + JobHopMeta greenHop = new JobHopMeta( ftpEntry, successEntry ); + greenHop.setEvaluation( true ); + jobMeta.addJobHop( greenHop ); + + + + return jobMeta; + + } catch ( Exception e ) { + + // something went wrong, just log and return + e.printStackTrace(); + return null; + } + } + + +} diff --git a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/RunningJobs.java b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/RunningJobs.java index 5eba91649..8f09c0386 100644 --- a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/RunningJobs.java +++ b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/RunningJobs.java @@ -68,7 +68,7 @@ public static void main( String[] args ) { instance = new RunningJobs(); // run a job from the file system - Job job = instance.runJobFromFileSystem( "etl/parameterized_job.kjb" ); + Job job = instance.runJobFromFileSystem( "etl/generated_ftp_job.kjb" ); // retrieve logging appender LoggingBuffer appender = KettleLogStore.getAppender(); @@ -146,7 +146,7 @@ public Job runJobFromFileSystem( String filename ) { Job job = new Job( null, jobMeta ); // adjust the log level - job.setLogLevel( LogLevel.MINIMAL ); + job.setLogLevel( LogLevel.ROWLEVEL ); System.out.println( "\nStarting job" ); diff --git a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlus.java b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlus.java new file mode 100644 index 000000000..6546f7b77 --- /dev/null +++ b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlus.java @@ -0,0 +1,116 @@ +package org.pentaho.di.sdk.samples.embedding.entries.ftpplus; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPReply; +import org.pentaho.di.cluster.SlaveServer; +import org.pentaho.di.core.Result; +import org.pentaho.di.core.annotations.JobEntry; +import org.pentaho.di.core.database.DatabaseMeta; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.exception.KettleXMLException; +import org.pentaho.di.core.xml.XMLHandler; +import org.pentaho.di.i18n.BaseMessages; +import org.pentaho.di.job.entry.JobEntryBase; +import org.pentaho.di.job.entry.JobEntryInterface; +import org.pentaho.di.repository.Repository; +import org.pentaho.metastore.api.IMetaStore; +import org.w3c.dom.Node; + +import java.io.IOException; +import java.net.SocketException; +import java.util.List; + +/** + * @author shepf + */ +@JobEntry( + id = "JobEntryFtpPlus", + name = "JobEntryFtpPlus.Name", + description = "JobEntryFtpPlus.TooltipDesc", + categoryDescription = "i18n:org.pentaho.di.job:JobCategory.Category.FileTransfer", + i18nPackageName = "org.pentaho.di.sdk.myplugins.jobentries.ftpplus", + documentationUrl = "JobEntryFtpPlus.DocumentationURL", + casesUrl = "JobEntryFtpPlus.CasesURL", + forumUrl = "JobEntryFtpPlus.ForumURL" +) +@Getter +@Setter +@ToString +public class JobEntryFtpPlus extends JobEntryBase implements Cloneable, JobEntryInterface { + + private String jsonConfStr = "{}"; + private String className = "JobEntryFtpPlus"; + private JobEntryFtpPlusParamsDO jobEntryFtpPlusParamsDO; + + /** + * for i18n + */ + private static Class PKG = JobEntryFtpPlus.class; + + public String getJsonConfStr() { + return jsonConfStr; + } + + public void setJsonConfStr(String jsonConfStr) { + this.jsonConfStr = jsonConfStr; + } + + + @Override + public Result execute(Result prev_result, int nr) throws KettleException { + + //TODO ftp业务代码 + log.logBasic("good: " + jobEntryFtpPlusParamsDO.toString()); + + + // indicate there are no errors + prev_result.setNrErrors( 0 ); + // indicate the result as configured + prev_result.setResult( true ); + return prev_result; + } + + + @Override + public String getXML() { + StringBuffer retval = new StringBuffer(); + + retval.append(super.getXML()); + JSONObject.toJSONString(jobEntryFtpPlusParamsDO); + + retval.append(" ").append( + XMLHandler.addTagValue("configInfo", jsonConfStr)); + retval.append(" ").append( + XMLHandler.addTagValue("className", className)); + + return retval.toString(); + } + + @Override + public void loadXML(Node entryNode, List databases, + List slaveServers, Repository rep, IMetaStore metaStore) + throws KettleXMLException { + try { + super.loadXML(entryNode, databases, slaveServers); + jsonConfStr = XMLHandler.getTagValue(entryNode, "configInfo"); + + //解析json到java类 + jobEntryFtpPlusParamsDO= JSON.parseObject(jsonConfStr,JobEntryFtpPlusParamsDO.class); + + className = XMLHandler.getTagValue(entryNode, "className"); + } catch (Exception e) { + throw new KettleXMLException(BaseMessages.getString(PKG, + "JobEntryKettleUtil.UnableToLoadFromXml"), e); + } + } + + + + +} diff --git a/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlusParamsDO.java b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlusParamsDO.java new file mode 100644 index 000000000..89134a404 --- /dev/null +++ b/kettle-sdk-embedding-samples/src/main/java/org/pentaho/di/sdk/samples/embedding/entries/ftpplus/JobEntryFtpPlusParamsDO.java @@ -0,0 +1,76 @@ +package org.pentaho.di.sdk.samples.embedding.entries.ftpplus; + + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * FtpPlus 下载插件参数 + * @author shepf + */ +@Getter +@Setter +@ToString +public class JobEntryFtpPlusParamsDO { + private String serverName; + private String userName; + private String password; + private String ftpDirectory; + private String targetDirectory; + private String wildcard; + private boolean binaryMode; + private int timeout; + private boolean remove; + private boolean onlyGettingNewFiles; /* Don't overwrite files */ + private boolean activeConnection; + private String controlEncoding; /* how to convert list of filenames e.g. */ + /** + * Implicit encoding used before PDI v2.4.1 + */ + private static String LEGACY_CONTROL_ENCODING = "US-ASCII"; + /** + * Default encoding when making a new ftp job entry instance. + */ + private static String DEFAULT_CONTROL_ENCODING = "ISO-8859-1"; + private boolean movefiles; + private String movetodirectory; + private boolean adddate; + private boolean addtime; + private boolean SpecifyFormat; + private String date_time_format; + private boolean AddDateBeforeExtension; + private boolean isaddresult; + private boolean createmovefolder; + private String port; + private String proxyHost; + private String proxyPort; /* string to allow variable substitution */ + private String proxyUsername; + private String proxyPassword; + private String socksProxyHost; + private String socksProxyPort; + private String socksProxyUsername; + private String socksProxyPassword; + public int ifFileExistsSkip = 0; + public String SifFileExistsSkip = "ifFileExistsSkip"; + public int ifFileExistsCreateUniq = 1; + public String SifFileExistsCreateUniq = "ifFileExistsCreateUniq"; + public int ifFileExistsFail = 2; + public String SifFileExistsFail = "ifFileExistsFail"; + public int ifFileExists; + public String SifFileExists; + public String SUCCESS_IF_AT_LEAST_X_FILES_DOWNLOADED = "success_when_at_least"; + public String SUCCESS_IF_ERRORS_LESS = "success_if_errors_less"; + public String SUCCESS_IF_NO_ERRORS = "success_if_no_errors"; + private String nr_limit; + private String success_condition; + long NrErrors = 0; + long NrfilesRetrieved = 0; + boolean successConditionBroken = false; + int limitFiles = 0; + String targetFilename = null; + static String FILE_SEPARATOR = "/"; + + + +} diff --git a/kettle-sdk-jobentry-plugin/pom.xml b/kettle-sdk-jobentry-plugin/pom.xml index f97318da8..617e18ae1 100644 --- a/kettle-sdk-jobentry-plugin/pom.xml +++ b/kettle-sdk-jobentry-plugin/pom.xml @@ -63,6 +63,28 @@ ${mockito.version} test + + + + com.alibaba + fastjson + 1.2.47 + + + + commons-net + commons-net + 3.6 + + + + org.projectlombok + lombok + 1.18.2 + provided + + + diff --git a/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlus.java b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlus.java new file mode 100644 index 000000000..a0bd8526f --- /dev/null +++ b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlus.java @@ -0,0 +1,147 @@ +package org.pentaho.di.sdk.myplugins.jobentries.ftpplus; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPReply; +import org.pentaho.di.cluster.SlaveServer; +import org.pentaho.di.core.Result; +import org.pentaho.di.core.annotations.JobEntry; +import org.pentaho.di.core.database.DatabaseMeta; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.exception.KettleXMLException; +import org.pentaho.di.core.xml.XMLHandler; +import org.pentaho.di.i18n.BaseMessages; +import org.pentaho.di.job.entry.JobEntryBase; +import org.pentaho.di.job.entry.JobEntryInterface; +import org.pentaho.di.repository.Repository; +import org.pentaho.metastore.api.IMetaStore; +import org.w3c.dom.Node; + +import java.io.IOException; +import java.util.List; + +/** + * configInfo json字符串格式 + * getDefaultConfigInfo() 格式化 json str + * @author shepf + */ +@JobEntry( + id = "JobEntryFtpPlus", + name = "JobEntryFtpPlus.Name", + description = "JobEntryFtpPlus.TooltipDesc", + categoryDescription = "i18n:org.pentaho.di.job:JobCategory.Category.FileTransfer", + i18nPackageName = "org.pentaho.di.sdk.myplugins.jobentries.ftpplus", + documentationUrl = "JobEntryFtpPlus.DocumentationURL", + casesUrl = "JobEntryFtpPlus.CasesURL", + forumUrl = "JobEntryFtpPlus.ForumURL" +) +@Getter +@Setter +@ToString +public class JobEntryFtpPlus extends JobEntryBase implements Cloneable, JobEntryInterface { + + private String configInfo = "{}"; + private String className = this.getClass().getName(); + private JobEntryFtpPlusParamsDO jobEntryFtpPlusParamsDO; + + /** + * for i18n + */ + private static Class PKG = JobEntryFtpPlus.class; + + + @Override + public Result execute(Result prev_result, int nr) throws KettleException { + + //TODO ftp业务代码 + log.logBasic("good: " + jobEntryFtpPlusParamsDO.toString()); + + FTPClient ftp = new FTPClient(); + FTPClientConfig config = new FTPClientConfig(); + + //config.setXXX(YYY); // change required options + // for example config.setServerTimeZoneId("Pacific/Pitcairn") + ftp.configure(config ); + boolean error = false; + try { + int reply; + String server = "127.0.0.1"; + ftp.connect(server); + ftp.login("ftp1",null); + + + System.out.println("Connected to " + server + "."); + System.out.print(ftp.getReplyString()); + + // After connection attempt, you should check the reply code to verify + // success. + reply = ftp.getReplyCode(); + + if(!FTPReply.isPositiveCompletion(reply)) { + ftp.disconnect(); + System.err.println("FTP server refused connection."); + System.exit(1); + } + // transfer files + ftp.logout(); + } catch(IOException e) { + error = true; + e.printStackTrace(); + } finally { + if(ftp.isConnected()) { + try { + ftp.disconnect(); + } catch(IOException ioe) { + // do nothing + } + } + System.exit(error ? 1 : 0); + } + + // indicate there are no errors + prev_result.setNrErrors( 0 ); + // indicate the result as configured + prev_result.setResult( true ); + return prev_result; + } + + + @Override + public String getXML() { + StringBuffer retval = new StringBuffer(); + + retval.append(super.getXML()); + JSONObject.toJSONString(jobEntryFtpPlusParamsDO); + + retval.append(" ").append( + XMLHandler.addTagValue("configInfo", configInfo)); + retval.append(" ").append( + XMLHandler.addTagValue("className", className)); + + return retval.toString(); + } + + @Override + public void loadXML(Node entryNode, List databases, + List slaveServers, Repository rep, IMetaStore metaStore) + throws KettleXMLException { + try { + super.loadXML(entryNode, databases, slaveServers); + configInfo = XMLHandler.getTagValue(entryNode, "configInfo"); + + //解析json到java类 + jobEntryFtpPlusParamsDO= JSON.parseObject(configInfo,JobEntryFtpPlusParamsDO.class); + + className = XMLHandler.getTagValue(entryNode, "className"); + } catch (Exception e) { + throw new KettleXMLException(BaseMessages.getString(PKG, + "JobEntryKettleUtil.UnableToLoadFromXml"), e); + } + } + +} diff --git a/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusDialog.java b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusDialog.java new file mode 100644 index 000000000..dc88af96d --- /dev/null +++ b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusDialog.java @@ -0,0 +1,377 @@ +package org.pentaho.di.sdk.myplugins.jobentries.ftpplus; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.apache.commons.lang.StringUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.widgets.*; +import org.pentaho.di.core.Const; +import org.pentaho.di.core.Props; +import org.pentaho.di.core.annotations.PluginDialog; +import org.pentaho.di.i18n.BaseMessages; +import org.pentaho.di.job.JobMeta; +import org.pentaho.di.job.entry.JobEntryDialogInterface; +import org.pentaho.di.job.entry.JobEntryInterface; +import org.pentaho.di.repository.Repository; +import org.pentaho.di.ui.core.gui.WindowProperty; +import org.pentaho.di.ui.core.widget.StyledTextComp; +import org.pentaho.di.ui.core.widget.TextVar; +import org.pentaho.di.ui.job.dialog.JobDialog; +import org.pentaho.di.ui.job.entry.JobEntryDialog; +import org.pentaho.di.ui.trans.step.BaseStepDialog; + +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * @author shepf + */ +@PluginDialog(id = "JobEntryFtpPlus", + image = "org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.svg", + pluginType = PluginDialog.PluginType.JOBENTRY) +public class JobEntryFtpPlusDialog extends JobEntryDialog implements JobEntryDialogInterface { + + /** + * for i18n + */ + private static Class PKG = JobEntryFtpPlus.class; + /** + * the job entry configuration object + */ + private JobEntryFtpPlus jobEntry; + + /** + * 界面 + */ + private Label wlName; + private Text wText; + private FormData fdlName, fdName; + private Label wlConfigInfo; + private StyledTextComp wConfigInfo; + private JEditorPane editPane; + private FormData fdlConfigInfo, fdConfigInfo; + private Label wlPosition; + private FormData fdlPosition; + private Button wOK, wGet, wCancel; + private Listener lsOK, lsGet, lsCancel; + private Shell shell; + private SelectionAdapter lsDef; + private boolean changed; + + /** + * 配置名称 + */ + private TextVar wClassName; + private Label wlClassName; + private FormData fdlClassName, fdClassName; + + /** + * Instantiates a new job entry dialog. + * + * @param parent the parent shell + * @param jobEntryInt the job entry interface + * @param rep the repository + * @param jobMeta + */ + public JobEntryFtpPlusDialog(Shell parent, JobEntryInterface jobEntryInt, Repository rep, JobMeta jobMeta) { + super(parent, jobEntryInt, rep, jobMeta); + // it is safe to cast the JobEntryInterface object to the object handled by this dialog + jobEntry = (JobEntryFtpPlus) jobEntryInt; + // ensure there is a default name for new job entries + if (this.jobEntry.getName() == null) { + this.jobEntry.setName(BaseMessages.getString(PKG, "FtpPlus.Default.Name")); + } + } + + @Override + public JobEntryInterface open() { + Shell parent = getParent(); + Display display = parent.getDisplay(); + + shell = new Shell(parent, props.getJobsDialogStyle()); + props.setLook(shell); + JobDialog.setShellImage(shell, jobEntry); + + ModifyListener lsMod = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + jobEntry.setChanged(); + } + }; + changed = jobEntry.hasChanged(); + + FormLayout formLayout = new FormLayout(); + formLayout.marginWidth = Const.FORM_MARGIN; + formLayout.marginHeight = Const.FORM_MARGIN; + + shell.setLayout(formLayout); + shell.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.Title")); + + int middle = props.getMiddlePct(); + int margin = Const.MARGIN; + + wGet = new Button(shell, SWT.PUSH); + wGet.setText("获取默认配置"); + wGet.setToolTipText("在输入类名称后再通过此按钮获取对应默认配置信息"); + + wOK = new Button(shell, SWT.PUSH); + wOK.setText(BaseMessages.getString(PKG, "System.Button.OK")); + wCancel = new Button(shell, SWT.PUSH); + wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel")); + + // at the bottom + BaseStepDialog.positionBottomButtons(shell, new Button[]{wOK, wCancel, wGet}, margin, null); + + // Label组件 + wlName = new Label(shell, SWT.RIGHT); + wlName.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.Jobname.Label")); + props.setLook(wlName); + fdlName = new FormData(); + fdlName.left = new FormAttachment(0, 0); + fdlName.right = new FormAttachment(middle, -margin); + fdlName.top = new FormAttachment(0, margin); + wlName.setLayoutData(fdlName); + wText = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + props.setLook(wText); + wText.addModifyListener(lsMod); + fdName = new FormData(); + fdName.left = new FormAttachment(middle, 0); + fdName.top = new FormAttachment(0, margin); + fdName.right = new FormAttachment(100, 0); + wText.setLayoutData(fdName); + + wlClassName = new Label(shell, SWT.RIGHT); + wlClassName.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.ClassName.Label") + " "); + props.setLook(wlClassName); + fdlClassName = new FormData(); + fdlClassName.left = new FormAttachment(0, 0); + fdlClassName.right = new FormAttachment(middle, -margin); + fdlClassName.top = new FormAttachment(wText, margin); + wlClassName.setLayoutData(fdlClassName); + + wClassName = new TextVar(jobEntry, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + props.setLook(wClassName); + wClassName.addModifyListener(lsMod); + fdClassName = new FormData(); + fdClassName.left = new FormAttachment(middle, 0); + fdClassName.top = new FormAttachment(wText, margin); + fdClassName.right = new FormAttachment(100, margin); + wClassName.setLayoutData(fdClassName); + + wlPosition = new Label(shell, SWT.NONE); + wlPosition.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.LineNr.Label", "0")); + props.setLook(wlPosition); + fdlPosition = new FormData(); + fdlPosition.left = new FormAttachment(0, 0); + fdlPosition.bottom = new FormAttachment(wOK, -margin); + wlPosition.setLayoutData(fdlPosition); + + // Script line + wlConfigInfo = new Label(shell, SWT.NONE); + wlConfigInfo.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.Script.Label")); + props.setLook(wlConfigInfo); + fdlConfigInfo = new FormData(); + fdlConfigInfo.left = new FormAttachment(0, 0); + fdlConfigInfo.top = new FormAttachment(wClassName, margin); + wlConfigInfo.setLayoutData(fdlConfigInfo); + wConfigInfo = + new StyledTextComp(jobEntry, shell, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL, ""); + //默认json配置 + wConfigInfo.setText("{}"); + props.setLook(wConfigInfo, Props.WIDGET_STYLE_FIXED); + wConfigInfo.addModifyListener(lsMod); + fdConfigInfo = new FormData(); + fdConfigInfo.left = new FormAttachment(0, 0); + fdConfigInfo.top = new FormAttachment(wlConfigInfo, margin); + fdConfigInfo.right = new FormAttachment(100, -10); + fdConfigInfo.bottom = new FormAttachment(wlPosition, -margin); + wConfigInfo.setLayoutData(fdConfigInfo); + wConfigInfo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent arg0) { + setPosition(); + } + + }); + + wConfigInfo.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + setPosition(); + } + + @Override + public void keyReleased(KeyEvent e) { + setPosition(); + } + }); + wConfigInfo.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + setPosition(); + } + + @Override + public void focusLost(FocusEvent e) { + setPosition(); + } + }); + wConfigInfo.addMouseListener(new MouseAdapter() { + @Override + public void mouseDoubleClick(MouseEvent e) { + setPosition(); + } + + @Override + public void mouseDown(MouseEvent e) { + setPosition(); + } + + @Override + public void mouseUp(MouseEvent e) { + setPosition(); + } + }); + wConfigInfo.addModifyListener(lsMod); + // Add listeners + lsCancel = new Listener() { + @Override + public void handleEvent(Event e) { + cancel(); + } + }; + lsOK = new Listener() { + @Override + public void handleEvent(Event e) { + ok(); + } + }; + lsGet = new Listener() { + @Override + public void handleEvent(Event e) { + String prettyConf = null; + String msg = "获取默认配置失败"; + try { + String confStr = jobEntry.getConfigInfo(); + + //格式化显示json + prettyConf = JSON.toJSONString(jobEntry.getJobEntryFtpPlusParamsDO(),SerializerFeature.PrettyFormat); + + } catch (Exception e1) { + msg = e1.getMessage(); + } + if (StringUtils.isBlank(prettyConf)) { + wConfigInfo.setText("{}"); + MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR); + mb.setMessage(msg); + mb.setText("错误"); + mb.open(); + } else { + wConfigInfo.setText(prettyConf); + } + } + }; + + wCancel.addListener(SWT.Selection, lsCancel); + wOK.addListener(SWT.Selection, lsOK); + wGet.addListener(SWT.Selection, lsGet); + + lsDef = new SelectionAdapter() { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + ok(); + } + }; + + wText.addSelectionListener(lsDef); + + // Detect X or ALT-F4 or something that kills this window... + shell.addShellListener(new ShellAdapter() { + @Override + public void shellClosed(ShellEvent e) { + cancel(); + } + }); + + getData(); + + BaseStepDialog.setSize(shell, 250, 250, false); + + shell.open(); + props.setDialogSize(shell, "JobEvalDialogSize"); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + return jobEntry; + } + + public void setPosition() { + + String scr = wConfigInfo.getText(); + int linenr = wConfigInfo.getLineAtOffset(wConfigInfo.getCaretOffset()) + 1; + int posnr = wConfigInfo.getCaretOffset(); + + // Go back from position to last CR: how many positions? + int colnr = 0; + while (posnr > 0 && scr.charAt(posnr - 1) != '\n' && scr.charAt(posnr - 1) != '\r') { + posnr--; + colnr++; + } + wlPosition.setText(BaseMessages.getString(PKG, "JobEntryKettleUtil.Position.Label", "" + linenr, "" + colnr)); + + } + + public void dispose() { + WindowProperty winprop = new WindowProperty(shell); + props.setScreen(winprop); + shell.dispose(); + } + + /** + * Copy information from the meta-data input to the dialog fields. + */ + public void getData() { + if (jobEntry.getName() != null) { + wText.setText(jobEntry.getName()); + } + if ( jobEntry.getClassName() != null ) { + wClassName.setText( jobEntry.getClassName() ); + } + if ( jobEntry.getConfigInfo() != null ) { + wConfigInfo.setText( jobEntry.getConfigInfo() ); + } + + wText.selectAll(); + wText.setFocus(); + } + + private void cancel() { + jobEntry.setChanged(changed); + jobEntry = null; + dispose(); + } + + private void ok() { + if (Const.isEmpty(wText.getText())) { + MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR); + mb.setText(BaseMessages.getString(PKG, "System.StepJobEntryNameMissing.Title")); + mb.setMessage(BaseMessages.getString(PKG, "System.JobEntryNameMissing.Msg")); + mb.open(); + return; + } + + jobEntry.setConfigInfo( wConfigInfo.getText() ); + //jobEntry.setClassName( wClassName.getText() ); + dispose(); + } + +} diff --git a/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusParamsDO.java b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusParamsDO.java new file mode 100644 index 000000000..6d132d2f6 --- /dev/null +++ b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/JobEntryFtpPlusParamsDO.java @@ -0,0 +1,74 @@ +package org.pentaho.di.sdk.myplugins.jobentries.ftpplus; + + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * FtpPlus 下载插件参数 + * @author shepf + */ +@Getter +@Setter +@ToString +public class JobEntryFtpPlusParamsDO { + private String serverName; + private String userName; + private String password; + private String ftpDirectory; + private String targetDirectory; + private String wildcard; + private boolean binaryMode; + private int timeout; + private boolean remove; + private boolean onlyGettingNewFiles; /* Don't overwrite files */ + private boolean activeConnection; + private String controlEncoding; /* how to convert list of filenames e.g. */ + /** + * Implicit encoding used before PDI v2.4.1 + */ + private static String LEGACY_CONTROL_ENCODING = "US-ASCII"; + /** + * Default encoding when making a new ftp job entry instance. + */ + private static String DEFAULT_CONTROL_ENCODING = "ISO-8859-1"; + private boolean movefiles; + private String movetodirectory; + private boolean adddate; + private boolean addtime; + private boolean SpecifyFormat; + private String date_time_format; + private boolean AddDateBeforeExtension; + private boolean isaddresult; + private boolean createmovefolder; + private String port; + private String proxyHost; + private String proxyPort; /* string to allow variable substitution */ + private String proxyUsername; + private String proxyPassword; + private String socksProxyHost; + private String socksProxyPort; + private String socksProxyUsername; + private String socksProxyPassword; + public int ifFileExistsSkip = 0; + public String SifFileExistsSkip = "ifFileExistsSkip"; + public int ifFileExistsCreateUniq = 1; + public String SifFileExistsCreateUniq = "ifFileExistsCreateUniq"; + public int ifFileExistsFail = 2; + public String SifFileExistsFail = "ifFileExistsFail"; + public int ifFileExists; + public String SifFileExists; + public String SUCCESS_IF_AT_LEAST_X_FILES_DOWNLOADED = "success_when_at_least"; + public String SUCCESS_IF_ERRORS_LESS = "success_if_errors_less"; + public String SUCCESS_IF_NO_ERRORS = "success_if_no_errors"; + private String nr_limit; + private String success_condition; + long NrErrors = 0; + long NrfilesRetrieved = 0; + boolean successConditionBroken = false; + int limitFiles = 0; + String targetFilename = null; + static String FILE_SEPARATOR = "/"; + +} diff --git a/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/samples/jobentries/demo/JobEntryDemo.java b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/samples/jobentries/demo/JobEntryDemo.java index fa49c81de..3fc5a6ec5 100644 --- a/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/samples/jobentries/demo/JobEntryDemo.java +++ b/kettle-sdk-jobentry-plugin/src/main/java/org/pentaho/di/sdk/samples/jobentries/demo/JobEntryDemo.java @@ -58,16 +58,16 @@ * */ @JobEntry( - id = "DemoJobEntry", - name = "DemoJobEntry.Name", - description = "DemoJobEntry.TooltipDesc", - image = "org/pentaho/di/sdk/samples/jobentries/demo/resources/demo.svg", - categoryDescription = "i18n:org.pentaho.di.job:JobCategory.Category.Conditions", - i18nPackageName = "org.pentaho.di.sdk.samples.jobentries.demo", - documentationUrl = "DemoJobEntry.DocumentationURL", - casesUrl = "DemoJobEntry.CasesURL", - forumUrl = "DemoJobEntry.ForumURL" - ) + id = "DemoJobEntry", + name = "DemoJobEntry.Name", + description = "DemoJobEntry.TooltipDesc", + image = "org/pentaho/di/sdk/samples/jobentries/demo/resources/demo.svg", + categoryDescription = "i18n:org.pentaho.di.job:JobCategory.Category.Conditions", + i18nPackageName = "org.pentaho.di.sdk.samples.jobentries.demo", + documentationUrl = "DemoJobEntry.DocumentationURL", + casesUrl = "DemoJobEntry.CasesURL", + forumUrl = "DemoJobEntry.ForumURL" +) public class JobEntryDemo extends JobEntryBase implements Cloneable, JobEntryInterface { /** diff --git a/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/messages/messages_en_US.properties b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/messages/messages_en_US.properties new file mode 100644 index 000000000..de74371fd --- /dev/null +++ b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/messages/messages_en_US.properties @@ -0,0 +1,13 @@ +#ý +JobEntryKettleUtil.Title=JsonConfJobEntryDialog +JobEntryKettleUtil.Jobname.Label=JobName +JobEntryKettleUtil.ClassName.Label=JobEntryClassName +JobEntryKettleUtil.Script.Label=JsonConfigure + + +# +JobEntryFtpPlus.Name=FtpPlus Download +JobEntryFtpPlus.TooltipDesc=Demo FtpPlusDialog Job Entry +JobEntryFtpPlus.DocumentationURL=https://help.pentaho.com/Documentation/6.1/0R0/0V0 +JobEntryFtpPlus.CasesURL=http://jira.pentaho.com/browse/PDI/ +JobEntryFtpPlus.ForumURL=http://forums.pentaho.com/ \ No newline at end of file diff --git a/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.png b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.png new file mode 100644 index 000000000..841591051 Binary files /dev/null and b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.png differ diff --git a/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.svg b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.svg new file mode 100644 index 000000000..95639ccf0 --- /dev/null +++ b/kettle-sdk-jobentry-plugin/src/main/resources/org/pentaho/di/sdk/myplugins/jobentries/ftpplus/resources/demo.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 5bdcfcb8d..79ab46938 100644 --- a/pom.xml +++ b/pom.xml @@ -38,11 +38,11 @@ Pentaho Public http://nexus.pentaho.org/content/groups/omni/ - true + false daily - true + false interval:15