소스 검색

提供更多案例

zhzhenqin 3 년 전
부모
커밋
2db84bc008

+ 34 - 0
feign_client/IDataPublisherService.java

@@ -76,4 +76,38 @@ public interface IDataPublisherService {
     @RequestLine("POST /api/pub/saveServiceParams")
     @Headers({"Content-Type: application/json", "Accept: application/json"})
     Map<String, Object> saveServiceParams(ServiceParamVO model);
+
+
+    /**
+     * 获取任务的正在运行的
+     *
+     * @param jobId
+     * @return
+     */
+    @RequestLine("GET /getExecutingJobs?jobId={jobId}")
+    Map<String, Object> getExecutingJobs(@Param("jobId") String jobId);
+
+
+    /**
+     * 分页获取规则
+     *
+     * @param page
+     * @param pageSize
+     * @return
+     */
+    @RequestLine(value = "GET /rule/getAllRules?page={page}&pageSize={pageSize}")
+    Map<String, Object> getAllRules(@Param(value = "page") int page,
+                                    @Param(value = "pageSize") int pageSize);
+
+
+    /**
+     * 上传文件,管理平台上传直接到 任务调度
+     *
+     * @param jobId 任务 ID
+     * @param file  上传文件
+     * @return 返回上传结果
+     */
+    @RequestLine("POST /upload/file")
+    Map<String, Object> uploadFile(@Param(value = "jobId") String jobId,
+                                   @Param("file") MultipartFile file);
 }

+ 269 - 0
feign_client/JobSchedulerServiceImpl.java

@@ -0,0 +1,269 @@
+package com.cpic.home.governor.service.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.alibaba.fastjson.JSON;
+import com.cpic.caf.compon.tech.utils.CharsetUtil;
+import com.cpic.home.governor.service.JobSchedulerService;
+
+import feign.Feign;
+import feign.Param;
+import feign.RequestLine;
+import feign.RequestTemplate;
+import feign.Response;
+import feign.Util;
+import feign.codec.Decoder;
+import feign.codec.Encoder;
+import feign.gson.GsonDecoder;
+
+/**
+ * 该类为 JobSchedulerService 实例,因需要调用 JobScheduler, 需远程执行接口。
+ *
+ *
+ * <pre>
+ *
+ * Created by zhenqin.
+ * User: zhenqin
+ * Date: 2019/3/26
+ * Time: 18:43
+ * Vendor: primeton.com
+ * To change this template use File | Settings | File Templates.
+ *
+ * </pre>
+ *
+ * @author zhenqin
+ */
+@Component("jobSchedulerService")
+public class JobSchedulerServiceImpl implements InitializingBean, FactoryBean<JobSchedulerService>,
+        MethodInterceptor {
+
+
+    /**
+     * JobScheduler 地址
+     */
+    @Value("${jobscheduler.url}")
+    String jobSchedulerUrl;
+
+
+    private HttpClient httpclient = null;
+
+
+    static Logger logger = LoggerFactory.getLogger(JobSchedulerServiceImpl.class);
+
+
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        HttpClient httpclient = HttpClientBuilder.create().build();
+        this.httpclient = httpclient;
+    }
+
+
+    @Override
+    public JobSchedulerService getObject() throws Exception {
+        JobSchedulerService jobSchedulerService = Feign.builder()
+                .encoder(feignEncoder())
+                .decoder(feignDecoder())
+                .target(JobSchedulerService.class, jobSchedulerUrl);
+
+        // AOP,拦截上传文件的接口
+        ProxyFactory proxyFactory = new ProxyFactory();
+        proxyFactory.addAdvice(this);
+        proxyFactory.setTarget(jobSchedulerService);
+
+        // 获得代理类
+        return (JobSchedulerService) proxyFactory.getProxy();
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return JobSchedulerService.class;
+    }
+
+
+    /**
+     * AOP 织入
+     *
+     * @param invocation
+     * @return
+     * @throws Throwable
+     */
+    @Override
+    public Object invoke(MethodInvocation invocation) throws Throwable {
+        Object[] arguments = invocation.getArguments();
+        boolean isMutilForm = false;
+        for (Object argument : arguments) {
+            // 文件
+            isMutilForm = argument instanceof File || argument instanceof MultipartFile;
+            if (isMutilForm) {
+                // 有一个是文件的,则是上传
+                break;
+            }
+        }
+        Object result = null;
+        if (!isMutilForm) {
+            // 普通表单
+            result = invocation.proceed();
+        } else {
+            // 文件上传
+            result = new HashMap<>();
+            Method method = invocation.getMethod();
+            RequestLine requestLine = method.getAnnotation(RequestLine.class);
+            if (requestLine != null) {
+                String[] split = requestLine.value().split("\\s+");
+                String url = jobSchedulerUrl + split[1];
+                Map<String, Object> form = new HashMap<>();
+                Parameter[] parameters = method.getParameters();
+                Annotation[][] annotations = method.getParameterAnnotations();
+                int i = 0;
+                for (Parameter parameter : parameters) {
+                    form.put(getFormName(parameter, annotations[i]), invocation.getArguments()[i]);
+                    i++;
+                }
+
+                // 提交表单
+                return executeRest(url, form);
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * 提交表单
+     *
+     * @param form 提交表单,携带文件
+     * @return
+     */
+    public Map<String, Object> executeRest(String url, Map<String, Object> form) {
+        HttpPost request = new HttpPost(url);
+        MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
+        InputStream in = null;
+        try {
+            for (Map.Entry<String, Object> entry : form.entrySet()) {
+                if (entry.getValue() instanceof File || entry.getValue() instanceof MultipartFile) {
+                    if (entry.getValue() instanceof File) {
+                        String name = ((File) entry.getValue()).getName();
+                        multipartEntityBuilder.addPart(entry.getKey(), new FileBody((File) entry.getValue()));
+                        multipartEntityBuilder.addPart("name", new StringBody(name, ContentType.MULTIPART_FORM_DATA));
+                    } else {
+                        MultipartFile uploadFile = (MultipartFile) entry.getValue();
+                        in = uploadFile.getInputStream();
+                        String name = ((MultipartFile) entry.getValue()).getOriginalFilename();
+                        multipartEntityBuilder.addPart(entry.getKey(), new InputStreamBody(in, ContentType.MULTIPART_FORM_DATA, name));
+                        multipartEntityBuilder.addPart("name", new StringBody(name, ContentType.MULTIPART_FORM_DATA));
+                    }
+                } else {
+                    multipartEntityBuilder.addPart(entry.getKey(),
+                            (entry.getValue() instanceof String ? new StringBody((String) entry.getValue(), ContentType.MULTIPART_FORM_DATA) :
+                                    new StringBody(String.valueOf(entry.getValue()), ContentType.MULTIPART_FORM_DATA)));
+                }
+            }
+
+            request.setEntity(multipartEntityBuilder.build());
+            HttpResponse response = httpclient.execute(request);
+            if (response.getStatusLine().getStatusCode() == 200) {
+                String json = EntityUtils.toString(response.getEntity(), CharsetUtil.CHARSET_UTF_8);
+                logger.info(json);
+                return JSON.parseObject(json);
+            } else {
+                logger.error("execute {} error.", request.getURI());
+            }
+        } catch (IOException e) {
+            logger.error("execute rest api error.", e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+
+                }
+            }
+        }
+        return new HashMap<>();
+    }
+
+
+    /**
+     * 获取该 参数的名称,@Param 注解名称,若没有选择字段名称
+     *
+     * @param parameter
+     * @param param
+     * @return
+     */
+    String getFormName(Parameter parameter, Annotation[] param) {
+        if (param == null || param.length == 0) {
+            return parameter.getName();
+        }
+
+        // 选择 param 的名称
+        Param params = (Param) param[0];
+        return params.value();
+    }
+
+
+    public void setJobSchedulerUrl(String jobSchedulerUrl) {
+        this.jobSchedulerUrl = jobSchedulerUrl;
+    }
+
+
+    public static Decoder feignDecoder() {
+        // JSON
+        GsonDecoder gsonDecoder = new GsonDecoder();
+        return (Response response, Type type) -> {
+            Response.Body body = response.body();
+            if (body == null) {
+                return null;
+            }
+            if (String.class.equals(type)) {
+                return Util.toString(body.asReader());
+            }
+            return gsonDecoder.decode(response, type);
+        };
+    }
+
+
+    public static Encoder feignEncoder() {
+        // JSON
+        return (Object object, Type bodyType, RequestTemplate template) -> {
+            if (bodyType == String.class) {
+                template.body((String)object);
+            } else if (bodyType == byte[].class) {
+                template.body((byte[]) object, CharsetUtil.CHARSET_UTF_8);
+            } else if (object instanceof Number) {
+                template.body(String.valueOf(object));
+            }
+
+            template.body(JSON.toJSONString(object));
+        };
+    }
+}

+ 54 - 0
hadoop-principal/HadoopConfigurer.java

@@ -0,0 +1,54 @@
+package com.primeton.damp.core.config;
+
+import com.primeton.damp.principal.HadoopPrincipal;
+import com.primeton.damp.principal.HadoopPrincipalFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.RawLocalFileSystem;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * <pre>
+ *
+ * Created by zhaopx.
+ * User: zhaopx
+ * Date: 2021/4/2
+ * Time: 15:54
+ *
+ * </pre>
+ *
+ * @author zhaopx
+ */
+@Configuration
+@Slf4j
+public class HadoopConfigurer {
+
+
+    /**
+     * hdfs
+     */
+    @Autowired
+    HadoopProperties hadoopProperties;
+
+    @Bean
+    public FileSystem fs() throws IOException {
+        String type = Optional.ofNullable(hadoopProperties.getHdfsType()).orElse("local");
+        if("local".equalsIgnoreCase(type)) {
+            // local
+            log.info("use local file system");
+            return new RawLocalFileSystem();
+        } else if("cluster".equalsIgnoreCase(type)) {
+            log.info("use hdfs filesystem");
+            final HadoopPrincipal hadoopPrincipal = HadoopPrincipalFactory.getHadoopPrincipal(hadoopProperties.getTenentName(), hadoopProperties.getUserKeytab(), hadoopProperties.getKrb5File());
+            final org.apache.hadoop.conf.Configuration conf = hadoopPrincipal.getConf(hadoopProperties.getCoreSite(), hadoopProperties.getHdfsSite());
+            return FileSystem.get(conf);
+        } else {
+            throw new IllegalStateException("unkown hadoop type: " + type);
+        }
+    }
+}

+ 58 - 0
hadoop-principal/HadoopProperties.java

@@ -0,0 +1,58 @@
+package com.primeton.damp.core.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * <pre>
+ *
+ * Created by zhaopx.
+ * User: zhaopx
+ * Date: 2021/4/2
+ * Time: 16:01
+ *
+ * </pre>
+ *
+ * @author zhaopx
+ */
+@Configuration
+@ConfigurationProperties(prefix = "hadoop")
+@Getter
+@Setter
+public class HadoopProperties {
+
+
+    /**
+     * hdfs 文件类型, cluster、local  用 localFile FS 模拟 HDFS
+     */
+    String hdfsType = "local";
+
+
+    /**
+     * 认证的租户名称
+     */
+    String tenentName;
+
+
+    /**
+     * hdfs coreSite
+     */
+    String coreSite;
+
+    /**
+     * hdfs Site
+     */
+    String hdfsSite;
+
+    /**
+     * user keytab
+     */
+    String userKeytab;
+
+    /**
+     * kerberos
+     */
+    String krb5File;
+}

+ 61 - 0
java-sftp-ftp/DataShardConfig.java

@@ -0,0 +1,61 @@
+package com.primeton.damp.config;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zhaopx
+ * @date 2021/4/7  17:14
+ */
+@Slf4j
+@Data
+@Component
+@ConfigurationProperties(ignoreUnknownFields = true, prefix = "datashard")
+public class DataShardConfig {
+
+    /**
+     * 文件系统
+     */
+    String type = "local";
+
+    /**
+     * Local,HDFS 等文件系统的基础路径
+     */
+    String basePath;
+
+    /**
+     * 上传后是否删除源文件
+     */
+    boolean delSrc = false;
+
+    /**
+     * 数据文件后缀
+     */
+    String fileSuffix = ".bcp";
+
+
+    /**
+     * ftp 配置
+     */
+    final Map<String, Object> ftp = new HashMap<>();
+
+
+    /**
+     * sftp 配置
+     */
+    final Map<String, Object> sftp = new HashMap<>();
+
+
+    public void setFtp(Map<String, Object> ftp) {
+        this.ftp.putAll(ftp);
+    }
+
+    public void setSftp(Map<String, Object> sftp) {
+        this.sftp.putAll(sftp);
+    }
+}

+ 0 - 12
java-sftp-ftp/FtpClientHandler.java

@@ -296,16 +296,4 @@ public class FtpClientHandler implements X11ClientHandler {
         ftpClient.disconnect();
     }
 
-    //test
-    public static void main(String[] args) throws IOException {
-        Properties map = new Properties();
-        String host = "192.168.30.143";
-        int port = 2121;
-        String username = "admin";
-        String password = "primeton000000";
-        FtpClientHandler handler = new FtpClientHandler(host, port, username, password, map);
-        System.out.println(handler.deleteFile("/test"));
-
-    }
-
 }

+ 0 - 26
java-sftp-ftp/SftpClientHandler.java

@@ -267,30 +267,4 @@ public class SftpClientHandler implements X11ClientHandler {
         logger.warn("close the file system");
         fs.close();
     }
-
-
-    public static void main(String[] args) {
-        Properties map = new Properties();
-        String host = "localhost";
-        int port = 2121;
-        String username = "admin";
-        String password = "admin";
-        SftpClientHandler handler = new SftpClientHandler(host, port, username, password, map);
-//        handler.changeWorkingDirectory("/sms");
-        List<Path> result = handler.getChildren("/");
-
-        /*
-        OutputStream out = handler.writeFile("/hello.txt", false);
-        IOUtils.copy(new FileInputStream("pom.xml"), out);
-        out.flush();
-        out.close();
-        */
-
-        handler.exists("/hello.txt");
-        handler.exists("/example_data_1225");
-        handler.exists("/world/example_data_1225");
-
-        handler.mkdirs("/world/example_data_1225");
-        handler.exists("/world/example_data_1225");
-    }
 }

+ 69 - 0
java-sftp-ftp/X11DatasetHandler.java

@@ -0,0 +1,69 @@
+package com.primeton.damp.core.dataset;
+
+import com.primeton.damp.config.DataShardConfig;
+import com.primeton.damp.fileclient.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.fs.FileSystem;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Properties;
+
+/**
+ * <pre>
+ *
+ * Created by zhaopx.
+ * User: zhaopx
+ * Date: 2021/3/31
+ * Time: 13:17
+ *
+ * </pre>
+ *
+ * @author zhaopx
+ */
+@Component
+@Slf4j
+public class X11DatasetHandler implements FactoryBean<X11ClientHandler> {
+
+
+    /**
+     * DataShard 配置
+     */
+    @Autowired
+    DataShardConfig dataShardConfig;
+
+
+    @Autowired
+    FileSystem fs;
+
+
+    @Override
+    public X11ClientHandler getObject() throws Exception {
+        if(StringUtils.isBlank(dataShardConfig.getType()) || "local".equalsIgnoreCase(dataShardConfig.getType())) {
+            return new LocalFsClientHandler(dataShardConfig.getBasePath());
+        } else if("hdfs".equalsIgnoreCase(dataShardConfig.getType())) {
+            return new HdfsClientHandler(fs);
+        } else if("sftp".equalsIgnoreCase(dataShardConfig.getType())) {
+            return new SftpClientHandler((String) dataShardConfig.getSftp().get("host"),
+                    (Integer) dataShardConfig.getSftp().get("port"),
+                    (String) dataShardConfig.getSftp().get("username"),
+                    (String) dataShardConfig.getSftp().get("password"),
+                    new Properties());
+        } else if("ftp".equalsIgnoreCase(dataShardConfig.getType())) {
+            return new FtpClientHandler((String) dataShardConfig.getFtp().get("host"),
+                    (Integer) dataShardConfig.getFtp().get("port"),
+                    (String) dataShardConfig.getFtp().get("username"),
+                    (String) dataShardConfig.getFtp().get("password"),
+                    new Properties());
+        } else {
+            throw new IllegalArgumentException("Illegal config datashard.type: " + dataShardConfig.getType());
+        }
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return X11ClientHandler.class;
+    }
+}

+ 548 - 0
jpa-sqltemplate/DAOServiceImpl.java

@@ -0,0 +1,548 @@
+/*
+ * Copyright 2009 by primedata Corporation.
+ * , 
+ * 
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * primedata Corporation ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with primedata.
+ */
+
+package com.primeton.dams.utils.template.impl;
+
+import com.primeton.dams.utils.template.CoreSQLParser;
+import com.primeton.dams.utils.template.HibernateStatementSessionFactoryBean;
+import com.primeton.dams.utils.template.IDAOService;
+import com.primeton.dams.utils.template.Page;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.SQLQuery;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.Transformers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+
+import javax.persistence.EntityManager;
+import javax.persistence.LockModeType;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * find开头的方法是HQL方法,query开头的是SQL方法。
+ * @author user
+ * @version 1.0  2007-1-27
+ *
+ * @author zhaopx
+ * @version 7.1
+ * @date 2020-08-25
+ */
+@Slf4j
+@Repository(value = "daoService")
+public class DAOServiceImpl implements IDAOService {
+
+	/**
+	 * LOG
+	 */
+	private static Logger logger = LoggerFactory.getLogger(DAOServiceImpl.class);
+
+	@Autowired
+	JdbcTemplate jdbcTemplate;
+
+	@PersistenceContext
+	EntityManager entityManager;
+
+	@Override
+	public JdbcTemplate getJdbcTemplate() {
+		return jdbcTemplate;
+	}
+
+	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
+		this.jdbcTemplate = jdbcTemplate;
+	}
+
+	public DAOServiceImpl() {
+		super();
+	}
+
+	/** Get HQL from xxxSql.xml file*/
+	@Override
+	public String getSQL(String name) {
+		return HibernateStatementSessionFactoryBean.getStatementText(name);
+	}
+	
+	/** Get dynamic HQL from xxxSql.xml file, voOrMap is a VO or a map.*/
+	@Override
+	public String getSQL(String name, Object voOrMap) {
+		return HibernateStatementSessionFactoryBean.getStatementText(name, voOrMap);
+	}
+	
+	/** Get dynamic Params from xxxSql.xml file, voOrMap is a VO or a map.*/
+	@Override
+	public Object[] getParam(String name, Object voOrMap) {
+		return HibernateStatementSessionFactoryBean.getStatementParam(name, voOrMap);
+	}
+	
+	/** Get dynamic Params from xxxSql.xml file, voOrMap is a VO or a map.*/
+	@Override
+	public Map getMapParam(String name, Object voOrMap) {
+		return HibernateStatementSessionFactoryBean.getStatementParamMap(name, voOrMap);
+	}
+	
+	//Persist the given transient instance
+    @Override
+	public void add(Object vo) throws DataAccessException {
+		entityManager.persist(vo);
+    }
+
+    //Update the given persistent instance
+    @Override
+	public void update(Object vo) throws DataAccessException {
+		entityManager.merge(vo);
+    }
+    
+    @Override
+	public void merge(Object entity) throws DataAccessException {
+		entityManager.merge(entity);
+    }
+
+    //Save or update the given persistent instance, according to its id
+    @Override
+	public void addOrUpdate(Object vo) throws DataAccessException {
+		entityManager.persist(vo);
+    }
+    
+    @Override
+	public void addAll(Collection vos) throws DataAccessException {
+		for (Object vo : vos) {
+			entityManager.persist(vo);
+		}
+	}
+
+    //Return the persistent instance of the given entity class with the given identifier,
+    //or null if not found
+    @Override
+	public Object get(Class entityClass, Serializable id) {
+		return entityManager.find(entityClass, id);
+    }
+
+    //Return the persistent instance of the given entity class with the given identifier,
+    //or null if not found
+    @Override
+	public Object get(Class entityClass, Serializable id, LockModeType lockMode) {
+        return entityManager.find(entityClass, id, lockMode);
+    }
+
+    //Delete the given persistent instance, according to its id
+    @Override
+	public void delete(Class entityClass, Serializable id) throws DataAccessException {
+		entityManager.remove(get(entityClass, id));
+    }
+
+    //Delete the given persistent instance
+    @Override
+	public void delete(Object entity) throws DataAccessException {
+		entityManager.remove(entity);
+    }
+
+    //Delete all given persistent instances
+    @Override
+	public void deleteAll(Collection entities) throws DataAccessException {
+		for (Object entity : entities) {
+			entityManager.remove(entity);
+		}
+    }
+
+
+    //Update/delete all objects according to the given query
+    @Override
+	public int execute(String updateSql) {
+		return entityManager.createNativeQuery(updateSql).executeUpdate();
+    }
+    
+    //Update/delete all objects according to the given query
+    @Override
+	public int execute(String updateSql, Object[] params) {
+		final javax.persistence.Query nativeQuery = entityManager.createNativeQuery(updateSql);
+		for (int i = 0; params != null && i < params.length; i++) {
+			nativeQuery.setParameter(i, params[i]);
+		}
+		return nativeQuery.executeUpdate();
+	}
+    
+    //flush session
+    @Override
+	public void flush() {
+    	entityManager.flush();
+    }
+    
+    //clear session
+    @Override
+	public void clear() {
+    	entityManager.clear();
+    }
+    
+
+    // count(*).
+    @Override
+	public int count(String queryString, Object[] values) {
+		return countSQL(queryString, values);
+	}
+    
+
+	/**
+	 * 执行SQL查询。提供结果集的转换接口ResultTransformer。
+	 * @param query
+	 * @param params
+	 * @param rt
+	 * @param firstResult
+	 * @param maxResult
+	 * @return
+	 */
+	@Override
+	public List query(javax.persistence.Query query, Object[] params, ResultTransformer rt, final int firstResult, final int maxResult) {
+		for (int i=0; params!=null && i<params.length; i++) {
+			query.setParameter(i, params[i]);
+		}
+		query.setMaxResults(maxResult);
+		query.setFirstResult(firstResult);
+		if (rt == null) {
+			query.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+		} else {
+			query.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(rt);
+		}
+		return query.getResultList();
+	}
+
+	@Override
+	public List query(javax.persistence.Query query, Object[] params, ResultTransformer rt) {
+		for (int i=0; params!=null && i<params.length; i++) {
+			query.setParameter(i, params[i]);
+		}
+		if (rt == null) {
+			query.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+		} else {
+			query.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(rt);
+		}
+		return query.getResultList();
+	}
+	
+	@Override
+	public List query(String sql, Object[] params, ResultTransformer rt) {
+		return query(entityManager.createNativeQuery(sql), params, rt);
+	}
+
+
+	/**
+	 * 执行SQL查询
+	 * @param id 脚本
+	 * @param params 参数
+	 * @param entityClass 转换
+	 * @return Object[]/Object
+	 */
+	@Override
+	public List queryById(String id, Map<String, Object> params, Class entityClass) {
+		final String sql = getSQL(id);
+		if(StringUtils.isBlank(sql)) {
+			throw new IllegalArgumentException("undefined statement id: " + id);
+		}
+		final Object[] sqlParams = (params == null ? null : getParam(id, params));
+		final Query nativeQuery = entityClass == null ? entityManager.createNativeQuery(sql) : entityManager.createNativeQuery(sql, entityClass);
+		if (entityClass == null) {
+			nativeQuery.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+		}
+		return query(nativeQuery, sqlParams, entityClass, null, null);
+	}
+
+	/**
+	 * 执行 statement id 的查询,返回  ResultTransformer 的转换结果, 并应用分页
+	 * @param id
+	 * @param params
+	 * @param entityClass
+	 * @param firstResult
+	 * @param maxResult
+	 * @return
+	 */
+	@Override
+	public List queryById(String id, Map<String, Object> params, Class entityClass, final int firstResult, final int maxResult) {
+		final String sql = getSQL(id);
+		if(StringUtils.isBlank(sql)) {
+			throw new IllegalArgumentException("undefined statement id: " + id);
+		}
+		final Object[] sqlParams = (params == null ? null : getParam(id, params));
+		final Query nativeQuery = entityClass == null ? entityManager.createNativeQuery(sql) : entityManager.createNativeQuery(sql, entityClass);
+		if (entityClass == null) {
+			nativeQuery.unwrap(org.hibernate.SQLQuery.class)
+					.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+		}
+		return query(nativeQuery, sqlParams, entityClass, firstResult, maxResult);
+	}
+
+
+	private List query(javax.persistence.Query query, Object[] params, Class entityClass, final Integer firstResult, final Integer maxResult) {
+		for (int i = 0; params != null && i < params.length; i++) {
+			query.setParameter(i, params[i]);
+		}
+		if (firstResult != null) {
+			query.setFirstResult(firstResult);
+		}
+		if (maxResult != null) {
+			query.setMaxResults(maxResult);
+		}
+		return query.getResultList();
+	}
+
+
+	@Override
+	public List querySQL(javax.persistence.Query query, Object[] params) {
+		return this.query(query, params, null);
+	}
+	
+	@Override
+	public List queryForMap(String sql, Object[] params) {
+		return this.query(sql, params, Transformers.ALIAS_TO_ENTITY_MAP);
+	}
+	
+	@Override
+	public List queryForMap(javax.persistence.Query query, Object[] params) {
+		return this.query(query, params, Transformers.ALIAS_TO_ENTITY_MAP);
+	}
+
+	@Override
+	public List queryForMap(String sql, Object[] params, int firstResult, int maxResult) {
+		final javax.persistence.Query query1 = entityManager.createNativeQuery(sql);
+		for (int i = 0; params != null && i < params.length; i++) {
+			query1.setParameter(i, params[i]);
+		}
+
+		query1.setFirstResult(firstResult);
+		query1.setMaxResults(maxResult);
+		query1.unwrap(org.hibernate.SQLQuery.class)
+				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
+		return query1.getResultList();
+	}
+	
+	private List queryForEntity(javax.persistence.Query query, Object[] params, Class entityClass) {
+		for (int i = 0; params != null && i < params.length; i++) {
+			query.setParameter(i, params[i]);
+		}
+		return query.getResultList();
+	}
+
+	@Override
+	public List queryForEntity(String sql, Class entityClass) {
+		final javax.persistence.Query query1 = entityManager.createQuery(sql, entityClass);
+		return query1.getResultList();
+	}
+
+	@Override
+	public List queryForEntity(String sql, Object[] params, Class entityClass) {
+		final javax.persistence.Query query1 = entityManager.createQuery(sql, entityClass);
+		return this.queryForEntity(query1, params, entityClass);
+	}
+	
+	@Override
+	public List queryForEntity(String sql, Object[] params,
+							   Class entityClass, int firstResult, int maxResult) {
+		final javax.persistence.Query query1 = entityManager.createQuery(sql, entityClass);
+		SQLQuery query = query1.unwrap(SQLQuery.class);
+		query.setMaxResults(maxResult).setFirstResult(firstResult);
+		return this.queryForEntity(query1, params, entityClass);
+	}
+
+	/**
+	 * 查询返回一条数据,无数据则返回 null
+	 * @param sql
+	 * @param params
+	 * @param entityClass
+	 * @param <T>
+	 * @return
+	 */
+	public <T> T queryForObject(String sql, Object[] params, Class<T> entityClass) {
+		final javax.persistence.Query query1 = entityManager.createQuery(sql, entityClass);
+		for (int i = 0; params != null && i < params.length; i++) {
+			query1.setParameter(i, params[i]);
+		}
+		try {
+			return (T) query1.getSingleResult();
+		} catch (NoResultException e) {
+			// 未查询到数据,则返回 null
+			return null;
+		} catch (NonUniqueResultException e) {
+			// 查询到多条,则返回第 0 条
+			return (T)query1.getResultList().get(0);
+		}
+	}
+
+	@Override
+	public Long queryForLong(String sql) {
+		return this.queryForLong(sql, null);
+	}
+	
+	@Override
+	public Long queryForLong(String sql, Object[] params) {
+		List list = this.querySQL(sql, params);
+		if (list != null && list.size() > 0 && list.get(0) != null) {
+			return new Long(((Number)list.get(0)).longValue());
+		}
+		return null;
+	}
+
+	@Override
+	public int updateById(String id, Map<String, Object> params) {
+		final String sql = getSQL(id);
+		if(StringUtils.isBlank(sql)) {
+			throw new IllegalArgumentException("undefined statement id: " + id);
+		}
+		final Object[] sqlParams = (params == null ? null : getParam(id, params));
+		SQLQuery query = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
+		if (sqlParams != null && sqlParams.length > 0) {
+			for (int i = 0; i < sqlParams.length; i++) {
+				query.setParameter(i, sqlParams[i]);
+			}
+		}
+		return query.executeUpdate();
+	}
+
+	@Override
+	public int updateSQL(String sql, final Object[] params) {
+		if (StringUtils.isBlank(sql)) {
+			return 0;
+		}
+		SQLQuery query = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
+		if (params != null && params.length > 0) {
+			for (int i = 0; i < params.length; i++) {
+				query.setParameter(i, params[i]);
+			}
+		}
+		return query.executeUpdate();
+	}
+	
+	@Override
+	public int[] batchUpdateSQL(String[] sql) {
+		int[] r = new int[sql.length];
+		for (int i = 0; i < sql.length; i++) {
+			r[i] = updateSQL(sql[i], null);
+		}
+		return r;
+	}
+
+	@Override
+	public List queryPageList(String sql, Object[] params, Page page) {
+		SQLQuery query = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
+		for (int i = 0; params != null && i < params.length; i++) {
+			query.setParameter(i, params[i]);
+		}
+		int totalCount = this.countSQL(sql, params);
+		query.setMaxResults(page.getPageSize());
+		query.setFirstResult(page.getStartIndex());
+		List list = new ArrayList();
+		if (totalCount > 0) {
+			list = query.list();
+		}
+		page.setCurrRecords(list.size());
+		page.setQueryResult(list);
+		page.setTotalRecs(totalCount);
+		return list;
+	}
+	
+	@Override
+	public List queryPageMap(String sql, Object[] params, Page page) {
+		int totalCount = this.countSQL(sql, params);
+		List<?> list = new ArrayList();
+		if (totalCount > 0) {
+			final javax.persistence.Query nativeQuery = entityManager.createNativeQuery(sql);
+			SQLQuery query = nativeQuery.unwrap(SQLQuery.class);
+			query.setMaxResults(page.getPageSize());
+			query.setFirstResult(page.getStartIndex());
+			return this.queryForMap(nativeQuery, params);
+		}
+		page.setCurrRecords(list.size());
+		page.setQueryResult(list);
+		page.setTotalRecs(totalCount);
+		return list;
+    }
+	
+	/*
+	 * (non-Javadoc)
+	 * @see com.primedata.primedatamm.core.dao.IDAOService
+	 * #queryPageEntity(java.lang.String, java.lang.Object[], 
+	 * com.primedata.primedatamm.core.common.Page, EntityList)
+	 */
+	@Override
+	public List queryPageEntity(String sql, Object[] params, Page page, Class entityClass) {
+		int totalCount = this.countSQL(sql, params);
+		List<?> list = new ArrayList();
+		if (totalCount > 0) {
+			final javax.persistence.Query nativeQuery = entityManager.createNativeQuery(sql, entityClass);
+			SQLQuery query = nativeQuery.unwrap(SQLQuery.class);
+			for (int i = 0; params != null && i < params.length; i++) {
+				query.setParameter(i, params[i]);
+			}
+			query.setMaxResults(page.getPageSize());
+			query.setFirstResult(page.getStartIndex());
+			return query.list();
+		}
+		page.setCurrRecords(list.size());
+		page.setQueryResult(list);
+		page.setTotalRecs(totalCount);
+		return list;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see com.primedata.primedatamm.core.dao.IDAOService#getCountSQL(java.lang.String)
+	 */
+	@Override
+	public String getCountSQL(String sql) {
+		return CoreSQLParser.getCountSQL(sql);
+	}
+
+    // count sql query result.
+    @Override
+	public int countSQL(String sql, Object[] params) {
+    	String strQuery = this.getCountSQL(sql);
+		int count = 0;
+		List<?> list = this.querySQL(strQuery, params);
+		if (list != null && list.get(0) != null) {// 取出count总数
+			count = ((Number)list.get(0)).intValue();
+		}
+		return count;
+	}
+    
+
+    // 创建statement的id=getSEQ_xxx,其SQL如: select $sequence$.nextval from dual
+    @Override
+	public Long getSQNextValue(String sequence) {
+    	Map<String,Object> map = new HashMap<String,Object>();
+    	map.put("sequence", sequence);
+		return queryForLong(getSQL("getSQNextValue", map), getParam("getSQNextValue", map));
+	}
+
+	@Override
+	public Long getSQCurrValue(String sequence) {
+		Map<String,Object> map = new HashMap<String,Object>();
+    	map.put("sequence", sequence);
+		return queryForLong(getSQL("getSQCurrValue", map), getParam("getSQCurrValue", map));
+	}
+}

+ 143 - 0
jpa-sqltemplate/DatabaseRecognizer.java

@@ -0,0 +1,143 @@
+/**
+ * 
+ */
+package com.primeton.dams.utils.template;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.InitializingBean;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * 数据库辨认,如将全称的“Microsoft SQL Server”辨认为简称“SQLServer”
+ * @author user
+ * @version 2.2 2010-10-21
+ */
+@Slf4j
+public class DatabaseRecognizer implements InitializingBean {
+
+	/**
+	 * 需要辨认的数据库名称,KEY为全称,VALUE为简称
+	 */
+	private Map<String,String> databaseNames = new HashMap<String, String>();
+
+	/**
+	 * 当前数据库类型
+	 */
+	String dbType;
+
+	/**
+	 * 当前元数据系统所使用的数据库的产品名称,如“Microsoft SQL Server”、“DB2/NT”、“DB2/LINUX”、“DB2/SUN”
+	 */
+	String currentDatabaseProductName;
+	
+	/**
+	 * 当前元数据系统所使用的数据库的产品版本
+	 */
+	String currentDatabaseProductVersion;
+
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+
+	}
+
+
+	/**
+	 * 获取数据库连接并取得数据库名称,然后选择合适的与数据库相关的Spring配置文件
+	 * @param dataSource dataSource
+	 * @return Spring配置文件名
+	 */
+	public String determineDatabase(DataSource dataSource) {
+		String databaseName = null;
+		try (Connection conn = dataSource.getConnection()){
+			DatabaseMetaData meta = conn.getMetaData();
+			databaseName = meta.getDatabaseProductName();
+			if(databaseName.equals("DM DBMS")){
+				databaseName = "DM";
+			}
+			String productVersion = meta.getDatabaseProductVersion();
+			if (log.isInfoEnabled()) {
+				log.info("Current Database is " + databaseName + ", Version is " + productVersion);
+			}
+			setCurrentDatabaseProductName(databaseName);
+			setCurrentDatabaseProductVersion(productVersion);
+			databaseName = StringUtils.lowerCase(getDatabaseName(databaseName));
+			this.dbType = databaseName;
+		} catch (SQLException e) {
+			String s = "Cannot determine the database brand name: " + databaseName;
+			log.error(s, e);
+			throw new IllegalArgumentException(s, e);
+		}
+		return databaseName;
+	}
+
+	/**
+	 * 返回数据库的简称
+	 * @param dbFullName 数据库全称
+	 * @return 简称,若没有找到则返回dbFullName
+	 */
+	public String getDatabaseName(String dbFullName) {
+		if (databaseNames.containsKey(dbFullName)) {
+			// 名称全匹配的情况
+			return databaseNames.get(dbFullName);
+		}
+		for (Iterator<String> it = databaseNames.keySet().iterator(); it.hasNext();) {
+			String key = it.next();
+			// 正则表达式匹配,如DB2的产品名称可能返回不一样,如:DB2/NT, DB2/LINUX, DB2/SUN
+			if (dbFullName.matches(key)) {
+				return databaseNames.get(key);
+			}
+		}
+		return dbFullName;
+	}
+
+	/**
+	 * 返回当前元数据系统所使用的数据库的产品名称,如“Microsoft SQL Server”、“DB2/NT”、“DB2/LINUX”
+	 * @return
+	 */
+	public String getCurrentDatabaseProductName() {
+		return this.currentDatabaseProductName;
+	}
+
+	/**
+	 * 设置当前元数据系统所使用的数据库的产品名称
+	 * @param currentDatabaseProductName
+	 */
+	public void setCurrentDatabaseProductName(String currentDatabaseProductName) {
+		this.currentDatabaseProductName = currentDatabaseProductName;
+	}
+
+	public String getCurrentDatabaseProductVersion() {
+		return currentDatabaseProductVersion;
+	}
+
+	public void setCurrentDatabaseProductVersion(
+			String currentDatabaseProductVersion) {
+		this.currentDatabaseProductVersion = currentDatabaseProductVersion;
+	}
+	
+	public Map<String, String> getDatabaseNames() {
+		return databaseNames;
+	}
+
+	public void setDatabaseNames(Map<String, String> databaseNames) {
+		this.databaseNames = databaseNames;
+	}
+
+
+	public String getDbType() {
+		return dbType;
+	}
+
+	public void setDbType(String dbType) {
+		this.dbType = dbType;
+	}
+}

+ 417 - 0
jpa-sqltemplate/IDAOService.java

@@ -0,0 +1,417 @@
+/*
+ * Copyright 2009 by primedata Corporation.
+ * , 
+ * 
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * primedata Corporation ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with primedata.
+ */
+
+package com.primeton.dams.utils.template;
+
+import org.hibernate.transform.ResultTransformer;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.persistence.LockModeType;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Data Access Implement Interface.
+ * find开头的方法是HQL方法,query开头的是SQL方法。
+ * @author user
+ * @version 1.0  2007-1-27
+ */
+// FIXME 迁移至core工程
+public interface IDAOService {
+	/** Get HQL from xxxSql.xml file*/
+	String getSQL(String name);
+	
+	/** Get dynamic HQL from xxxSql.xml file, voOrMap is a VO or a map.*/
+	String getSQL(String name, Object voOrMap);
+	
+	/** Get dynamic Params from xxxSql.xml file, voOrMap is a VO or a map.*/
+	Object[] getParam(String name, Object voOrMap);
+	
+	/** Get dynamic Params from xxxSql.xml file, voOrMap is a VO or a map.*/
+	Map getMapParam(String name, Object voOrMap);
+	
+	/**Persist the given transient instance*/
+    void add(Object vo) throws DataAccessException;
+
+    /**Update the given persistent instance*/
+    void update(Object vo) throws DataAccessException;
+
+    /**
+     * hibernate merge
+     * @param entity
+     * @throws DataAccessException
+     */
+    void merge(Object entity) throws DataAccessException;
+    
+    /**Save or update the given persistent instance, according to its id */
+    void addOrUpdate(Object vo) throws DataAccessException;
+    
+    /**Save or update the given persistent instances*/
+    void addAll(Collection vos) throws DataAccessException;
+
+    /**Return the persistent instance of the given entity class with the given identifier,
+     *or null if not found*/
+    Object get(Class entityClass, Serializable id);
+    
+    /**Return the persistent instance of the given entity class with the given identifier,
+     *or null if not found*/
+    Object get(Class entityClass, Serializable id, LockModeType lockMode);
+    
+    /**Delete the given persistent instance, according to its id*/
+    void delete(Object entity) throws DataAccessException;
+
+    /**Delete the given persistent instance*/
+    void delete(Class entityClass, Serializable id) throws DataAccessException;
+
+    /**Delete all given persistent instances*/
+    void deleteAll(Collection entities) throws DataAccessException;
+
+    /**Update/delete all objects according to the given query*/
+    int execute(String updateSql);
+    
+    /**Update/delete all objects according to the given query*/
+    int execute(String updateSql, Object[] params);
+    
+    /**flush*/
+    void flush();
+    
+    /**clear*/
+    void clear();
+    
+
+    /** count(*). */
+    int count(String queryString, Object[] parameters);
+    
+	/**
+	 * 执行SQL查询。提供结果集的转换接口ResultTransformer。
+	 * @param query Query 
+	 * @param params  参数
+	 * @param rt ResultTransformer
+	 * @return
+	 */
+	List query(javax.persistence.Query query, Object[] params, ResultTransformer rt);
+
+
+    /**
+     * 执行SQL查询。提供结果集的转换接口ResultTransformer。
+     * @param query
+     * @param params
+     * @param rt
+     * @param firstResult
+     * @param maxResult
+     * @return
+     */
+    public List query(javax.persistence.Query query, Object[] params, ResultTransformer rt, final int firstResult, final int maxResult);
+
+	/**
+	 * 执行SQL查询。提供结果集的转换接口ResultTransformer。
+	 * @param sql 脚本
+	 * @param params 参数
+	 * @param rt ResultTransformer
+	 * @return
+	 */
+	List query(String sql, Object[] params, ResultTransformer rt);
+
+
+    /**
+     * 执行SQL查询
+     * @param id statement id
+     * @return Object[]/Object
+     */
+    default List queryById(String id) {
+        return queryById(id, null, null);
+    }
+
+    /**
+     * 执行SQL查询
+     * @param id 脚本
+     * @param params 参数
+     * @return Object[]/Object
+     */
+    default List queryById(String id, Map<String, Object> params){
+        return queryById(id, params, null);
+    }
+
+
+    /**
+     * 执行 statement id 的查询,返回  ResultTransformer 的转换结果
+     * @param id statement id
+     * @param params 参数
+     * @param entityClass 结果转换
+     * @return 返回 ResultTransformer 的转换结果
+     */
+    public List queryById(String id, Map<String, Object> params, Class entityClass);
+
+
+    /**
+     * 执行 statement id 的查询,返回  ResultTransformer 的转换结果, 并应用分页
+     * @param id
+     * @param params
+     * @param entityClass
+     * @param firstResult
+     * @param maxResult
+     * @return
+     */
+    public List queryById(String id, Map<String, Object> params, Class entityClass, final int firstResult, final int maxResult);
+
+
+	/**
+	 * 执行SQL查询
+	 * @param sql 脚本
+	 * @return Object[]/Object
+	 */
+	default List querySQL(String sql) {
+	    return querySQL(sql, null);
+    }
+
+	/**
+	 * 执行SQL查询
+	 * @param sql 脚本
+	 * @param params 参数
+	 * @return Object[]/Object
+	 */
+    default List querySQL(String sql, Object[] params) {
+        return query(sql, params, null);
+    }
+
+    /**
+	 * 执行SQL查询Query
+	 * @param query Query,脚本
+	 * @param params 参数
+	 * @return Object[]/Object
+	 */
+    List querySQL(javax.persistence.Query query, Object[] params);
+    
+    /**
+	 * 执行SQL查询
+	 * @param sql 脚本
+	 * @return Map
+	 */
+    default List queryForMap(String sql) {
+        return queryForMap(sql, null);
+    }
+    
+    /**
+	 * 执行SQL查询
+	 * @param sql 脚本
+	 * @param params 参数
+	 * @return Map
+	 */
+    List queryForMap(String sql, Object[] params);
+    
+    /**
+     * 执行SQL查询 
+     * @param query Query
+     * @param params 参数
+     * @return
+     */
+    List queryForMap(javax.persistence.Query query, Object[] params);
+    
+    /**
+     * 执行SQL查询 
+     * @param sql 脚本
+     * @param params 参数
+     * @param firstResult 开始位置
+     * @param maxResult 最大记录数
+     * @return
+     */
+    List queryForMap(String sql, Object[] params, int firstResult, int maxResult);
+
+
+    /**
+     * 查询SQL返回实体。用法介绍:<br>
+     * <pre>
+     * sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+     * .addEntity("cat", Cat.class)
+     * .addEntity("mother", Cat.class)
+     * </pre>
+     * @param sql 脚本
+     * @param entityClass 实体类
+     * @return entity List
+     */
+    public List queryForEntity(String sql, Class entityClass);
+
+    /**
+     * 查询SQL返回实体。用法介绍:<br>
+     * <pre>
+     * sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+     * .addEntity("cat", Cat.class)
+     * .addEntity("mother", Cat.class)
+     * </pre>
+     * @param sql 脚本
+     * @param params 参数
+     * @param entityClass 实体类
+     * @return entity List
+     */
+    List queryForEntity(String sql, Object[] params, Class entityClass);
+    
+    /**
+     * 查询SQL返回实体。用法介绍:<br>
+     * <pre>
+     * sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+     * .addEntity("cat", Cat.class)
+     * .addEntity("mother", Cat.class)
+     * </pre>
+     * @param sql 脚本
+     * @param params 参数
+     * @param entityClass 实体类
+     * @param firstResult 开始位置
+     * @param maxResult 最大记录数
+     * @return entity List
+     */
+    List queryForEntity(String sql, Object[] params, Class entityClass, int firstResult, int maxResult);
+
+
+    /**
+     * 查询返回一条数据,无数据则返回 null
+     * @param sql
+     * @param entityClass DO 实体类,或者 Map
+     * @param <T>
+     * @return
+     */
+    default public <T> T queryForObject(String sql, Class<T> entityClass) {
+        return queryForObject(sql, null, entityClass);
+    }
+
+    /**
+     * 查询返回一条数据,无数据则返回 null
+     * @param sql
+     * @param params
+     * @param entityClass DO 实体类,或者 Map
+     * @param <T>
+     * @return
+     */
+    public <T> T queryForObject(String sql, Object[] params, Class<T> entityClass);
+
+
+    /**
+     * 查询整数值
+     * @param sql 脚本
+     * @return Long
+     */
+    Long queryForLong(String sql);
+    
+    /**
+     * 查询整数值
+     * @param sql 脚本
+     * @param params 参数
+     * @return Long
+     */
+    Long queryForLong(String sql, Object[] params);
+
+    /**
+     * 执行SQL更新、删除
+     * @param id 脚本
+     * @return
+     */
+    default int updateById(String id){
+        return updateById(id, null);
+    }
+
+
+    /**
+     * 执行SQL更新、删除
+     * @param id statement id
+     * @param params 参数
+     * @return
+     */
+    int updateById(String id, Map<String, Object> params);
+    
+    /**
+     * 执行SQL更新、删除
+     * @param sql 脚本
+     * @param params 参数
+     * @return
+     */
+    int updateSQL(String sql, Object[] params);
+    
+    /**
+     * 批量执行SQL
+     * @param sql SQL
+     * @return int[]
+     */
+    int[] batchUpdateSQL(String[] sql);
+    
+    /**
+     * 分页查询SQL
+     * @param sql 脚本
+     * @param params 参数
+     * @param page 分页
+     * @return Object[] List
+     */
+    List queryPageList(String sql, Object[] params, Page page);
+    
+    /**
+     * 分页查询SQL
+     * @param sql 脚本
+     * @param params 参数
+     * @param page 分页
+     * @return map List
+     */
+    List queryPageMap(String sql, Object[] params, Page page);
+    
+    /**
+     * 分页查询SQL,返回实体。用法介绍:<br>
+     * <pre>
+     * sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+     * .addEntity("cat", Cat.class)
+     * .addEntity("mother", Cat.class)
+     * </pre>
+     * @param sql 脚本
+     * @param params 参数
+     * @param page 分页参数
+     * @param entityClass 实体集合
+     * @return entity List
+     */
+    List queryPageEntity(String sql, Object[] params, Page page, Class entityClass);
+	
+    /**
+     * 解析SQL转换成count(*)的 统计脚本
+     * @param sql 原始SQL
+     * @return 统计SQL
+     */
+    String getCountSQL(String sql);
+    
+    /**
+     * 统计查询SQL
+     * @param sql 脚本
+     * @param params 参数
+     * @return count(*)的结果
+     */
+    int countSQL(String sql, Object[] params);
+    
+    /**
+     * (Oracle)获取序列号的下一个值
+     * @param sequence
+     * @return
+     */
+    Long getSQNextValue(String sequence);
+    
+    /**
+     * (Oracle)获取序列号的当前值
+     * @param sequence
+     * @return
+     */
+    Long getSQCurrValue(String sequence);
+    
+    /**
+     * 获取SimpleJdbcTemplate
+     * @return SimpleJdbcTemplate
+     */
+    JdbcTemplate getJdbcTemplate();
+
+}
+

+ 162 - 0
spring-bean/SpringContextHelper.java

@@ -0,0 +1,162 @@
+/*
+ * Copyright 2009 by primedata Corporation.
+ * ,
+ *
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * primedata Corporation ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with primedata.
+ */
+
+package com.primeton.dgs.kernel.core.common;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Spring上下文帮助类,方便获取bean、message等
+ *
+ * @author user
+ * @version 1.0 Date: 2009-7-7
+ */
+@Slf4j
+public class SpringContextHelper implements ApplicationContextAware {
+	/**
+	 * Spring上下文
+	 */
+	private static ApplicationContext applicationContext;
+
+	/**
+	 * 获取Spring上下文
+	 *
+	 * @return Spring Application Context
+	 */
+	public static ApplicationContext getSpringContext() {
+		return applicationContext;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.springframework.context.ApplicationContextAware
+	 * #setApplicationContext(org.springframework.context.ApplicationContext)
+	 */
+	public void setApplicationContext(ApplicationContext applicationContext)
+			throws BeansException {
+		SpringContextHelper.applicationContext = applicationContext;
+	}
+
+	/**
+	 * 获取定义在Spring配置文件里的bean实例
+	 *
+	 * @param beanName Spring配置文件里的bean注册名
+	 * @return bean实例(没有找到返回null)
+	 */
+	public static <T> T getBean(String beanName) {
+		return (T) applicationContext.getBean(beanName);
+	}
+
+	/**
+	 * 获取类型为requiredType的对象<br>
+	 * 如果bean不能被类型转换,相应的异常将会被抛出(BeanNotOfRequiredTypeException)
+	 *
+	 * @param beanName     Spring配置文件里的bean注册名
+	 * @param requiredType 返回对象类型
+	 * @return bean实例(没有找到返回null)
+	 */
+	public static <T> T getBean(String beanName, Class<T> requiredType) throws BeansException {
+		return applicationContext.getBean(beanName, requiredType);
+	}
+
+	public static <T> T getBean(Class<T> requiredType) throws BeansException {
+		return applicationContext.getBean(requiredType);
+	}
+
+	public static <T> T getBean(String name, Object... args) throws BeansException {
+		return (T) applicationContext.getBean(name, args);
+	}
+
+	public static <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
+		return applicationContext.getBean(requiredType, args);
+	}
+
+	/**
+	 * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+	 *
+	 * @param name Spring配置文件里的bean注册名
+	 * @return boolean
+	 */
+	public static boolean containsBean(String name) {
+		return applicationContext.containsBean(name);
+	}
+
+	/**
+	 * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
+	 * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+	 *
+	 * @param name Spring配置文件里的bean注册名
+	 * @return boolean 是否单例
+	 * @throws NoSuchBeanDefinitionException 没有定义Bean
+	 */
+	public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+		return applicationContext.isSingleton(name);
+	}
+
+	/**
+	 * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+	 *
+	 * @param name Spring配置文件里的bean注册名
+	 * @return 别名
+	 * @throws NoSuchBeanDefinitionException 没有定义Bean
+	 */
+	public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+		return applicationContext.getAliases(name);
+	}
+
+	/**
+	 * 手动注入Spring Bean中
+	 *
+	 * @param name               BeanName
+	 * @param clazz              注册的bean的类性
+	 * @param args               构造方法的必要参数,顺序和类型要求和clazz中定义的一致
+	 * @param <T>
+	 * @return 返回注册到容器中的bean对象
+	 */
+	public static <T> T registerBean(String name, Class<T> clazz, Object... args) {
+		ConfigurableApplicationContext context = null;
+		if (applicationContext instanceof ConfigurableApplicationContext) {
+			context = ((ConfigurableApplicationContext) applicationContext);
+		}
+		log.info("Register {} to spring context...",clazz.getCanonicalName());
+		if (context != null) {
+			if (context.containsBean(name)) {
+				Object bean = context.getBean(name);
+				if (bean.getClass().isAssignableFrom(clazz)) {
+					return (T) bean;
+				} else {
+					throw new RuntimeException("BeanName 重复 " + name);
+				}
+			}
+
+			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
+			for (Object arg : args) {
+				beanDefinitionBuilder.addConstructorArgValue(arg);
+			}
+			BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
+			BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) context.getBeanFactory();
+			beanFactory.registerBeanDefinition(name, beanDefinition);
+			return context.getBean(name, clazz);
+		}
+		return null;
+	}
+}

+ 39 - 0
spring-ldap/AuthenticationType.java

@@ -0,0 +1,39 @@
+/*
+ * 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.dolphinscheduler.api.security;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+
+/**
+ * authentication type
+ */
+public enum AuthenticationType {
+
+    PASSWORD(0, "verify via user name and password"),
+    LDAP(1, "verify via LDAP server"),
+    ;
+
+    AuthenticationType(int code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    @EnumValue
+    private final int code;
+    private final String desc;
+}

+ 43 - 0
spring-ldap/Authenticator.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.dolphinscheduler.api.security;
+
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface Authenticator {
+    /**
+     * Verifying legality via username and password
+     * @param username user name
+     * @param password user password
+     * @param extra extra info
+     * @return result object
+     */
+    Result<Map<String, String>> authenticate(String username, String password, String extra);
+
+    /**
+     * Get authenticated user
+     * @param request http servlet request
+     * @return user
+     */
+    User getAuthUser(HttpServletRequest request);
+}

+ 54 - 0
spring-ldap/Person.java

@@ -0,0 +1,54 @@
+package com.primeton.poctag.service.ldap;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.ldap.odm.annotations.Attribute;
+import org.springframework.ldap.odm.annotations.DnAttribute;
+import org.springframework.ldap.odm.annotations.Entry;
+import org.springframework.ldap.odm.annotations.Id;
+
+import javax.naming.Name;
+/**
+ * <pre>
+ *
+ * Created by zhenqin.
+ * User: zhenqin
+ * Date: 2021/9/14
+ * Time: 17:24
+ * Vendor: yiidata.com
+ * To change this template use File | Settings | File Templates.
+ *
+ * </pre>
+ *
+ * @author zhenqin
+ */
+@Setter
+@Getter
+@ToString
+@Entry(objectClasses = {"top", "person" ,"inetOrgPerson","organizationalPerson"})
+public class Person {
+
+    @Id
+    private Name dn;
+
+    //必填
+    @Attribute(name="cn")
+    private String cn;
+
+    @Attribute(name="sn")
+    private String sn;
+
+    @Attribute(name="uid")
+    private String uid;
+
+    //选填
+    @Attribute(name="userPassword")
+    private String password;
+
+    @Attribute(name="givenName")
+    String givenName;
+
+    @Attribute(name="description")
+    String description;
+}

+ 70 - 0
spring-ldap/PersonRepoImpl.java

@@ -0,0 +1,70 @@
+package com.primeton.poctag.service.ldap.impl;
+
+import com.primeton.poctag.service.ldap.Person;
+import com.primeton.poctag.service.ldap.PersonRepo;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.ldap.core.LdapTemplate;
+
+import javax.naming.InvalidNameException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.springframework.ldap.query.LdapQueryBuilder.query;
+
+/**
+ * <pre>
+ *
+ * Created by zhenqin.
+ * User: zhenqin
+ * Date: 2021/9/14
+ * Time: 16:41
+ * Vendor: yiidata.com
+ *
+ * </pre>
+ *
+ * @author zhenqin
+ */
+public class PersonRepoImpl implements PersonRepo {
+    private LdapTemplate ldapTemplate;
+
+    public void setLdapTemplate(LdapTemplate ldapTemplate) {
+        this.ldapTemplate = ldapTemplate;
+    }
+
+    @Override
+    public List<String> getAllPersonNames() {
+        /*
+        return ldapTemplate.search(
+                query().where("uid").is("zhenqin"),
+                new AttributesMapper<String>() {
+                    public String mapFromAttributes(Attributes attrs)
+                            throws NamingException {
+                        return attrs.get("uid").get().toString();
+                    }
+                });
+
+         */
+
+        final List<Person> people = ldapTemplate.find(query().where("uid").isPresent(), Person.class);
+        System.out.println(people);
+        return people.stream().map(Person::getUid).collect(Collectors.toList());
+    }
+
+
+    public static void main(String[] args) throws InvalidNameException {
+        ApplicationContext context = new ClassPathXmlApplicationContext("spring-ldap-test.xml");
+        final PersonRepoImpl personRepo = context.getBean(PersonRepoImpl.class);
+        System.out.println(personRepo.getAllPersonNames());
+
+        /*
+        Person vo = new Person();
+        vo.setDn(new LdapName("uid=admin"));
+        vo.setCn("admin");
+        vo.setSn("admin");
+        vo.setUid("admin");
+        vo.setPassword("123456");
+        personRepo.ldapTemplate.create(vo);
+         */
+    }
+}

+ 75 - 0
spring-ldap/SecurityConfig.java

@@ -0,0 +1,75 @@
+/*
+ * 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.dolphinscheduler.api.security;
+
+import org.apache.dolphinscheduler.api.security.impl.ldap.LdapAuthenticator;
+import org.apache.dolphinscheduler.api.security.impl.pwd.PasswordAuthenticator;
+
+import org.apache.commons.lang.StringUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SecurityConfig {
+    private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
+
+    @Value("${security.authentication.type:PASSWORD}")
+    private String type;
+
+    private AutowireCapableBeanFactory beanFactory;
+    private AuthenticationType authenticationType;
+
+    @Autowired
+    public SecurityConfig(AutowireCapableBeanFactory beanFactory) {
+        this.beanFactory = beanFactory;
+    }
+
+    private void setAuthenticationType(String type) {
+        if (StringUtils.isBlank(type)) {
+            logger.info("security.authentication.type configuration is empty, the default value 'PASSWORD'");
+            this.authenticationType = AuthenticationType.PASSWORD;
+            return;
+        }
+
+        this.authenticationType = AuthenticationType.valueOf(type);
+    }
+
+    @Bean(name = "authenticator")
+    public Authenticator authenticator() {
+        setAuthenticationType(type);
+        Authenticator authenticator;
+        switch (authenticationType) {
+            case PASSWORD:
+                authenticator = new PasswordAuthenticator();
+                break;
+            case LDAP:
+                authenticator = new LdapAuthenticator();
+                break;
+            default:
+                throw new IllegalStateException("Unexpected value: " + authenticationType);
+        }
+        beanFactory.autowireBean(authenticator);
+        return authenticator;
+    }
+}

+ 93 - 0
spring-ldap/TraditionalPersonRepoImpl.java

@@ -0,0 +1,93 @@
+package com.primeton.poctag.service.ldap.impl;
+
+import com.primeton.poctag.service.ldap.PersonRepo;
+
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * <pre>
+ *
+ * Created by zhenqin.
+ * User: zhenqin
+ * Date: 2021/9/14
+ * Time: 16:40
+ * Vendor: yiidata.com
+ *
+ * </pre>
+ *
+ * @author zhenqin
+ */
+public class TraditionalPersonRepoImpl implements PersonRepo {
+
+    @Override
+    public List<String> getAllPersonNames() {
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://localhost:10389/ou=yiidata,dc=yiidata,dc=com");
+
+        DirContext ctx;
+        try {
+            ctx = new InitialDirContext(env);
+        } catch (NamingException e) {
+            throw new RuntimeException(e);
+        }
+
+        List<String> list = new LinkedList<String>();
+        NamingEnumeration results = null;
+        try {
+            SearchControls controls = new SearchControls();
+            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            results = ctx.search("", "(uid=*)", controls);
+
+            System.out.println("-------------------------------");
+            while (results.hasMore()) {
+                SearchResult searchResult = (SearchResult) results.next();
+                Attributes attributes = searchResult.getAttributes();
+                System.out.println(attributes);
+                Attribute attr = attributes.get("uid");
+                String cn = attr.get().toString();
+                list.add(cn);
+            }
+        } catch (NameNotFoundException e) {
+            // The base context was not found.
+            e.printStackTrace();
+            // Just clean up and exit.
+        } catch (NamingException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (results != null) {
+                try {
+                    results.close();
+                } catch (Exception e) {
+                    // Never mind this.
+                }
+            }
+            if (ctx != null) {
+                try {
+                    ctx.close();
+                } catch (Exception e) {
+                    // Never mind this.
+                }
+            }
+        }
+        return list;
+    }
+
+
+    public static void main(String[] args) {
+        final List<String> allPersonNames = new TraditionalPersonRepoImpl().getAllPersonNames();
+        System.out.println(allPersonNames);
+    }
+}

+ 99 - 0
spring-ldap/impl/AbstractAuthenticator.java

@@ -0,0 +1,99 @@
+/*
+ * 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.dolphinscheduler.api.security.impl;
+
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.security.Authenticator;
+import org.apache.dolphinscheduler.api.service.SessionService;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.common.Constants;
+import org.apache.dolphinscheduler.common.enums.Flag;
+import org.apache.dolphinscheduler.dao.entity.Session;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractAuthenticator implements Authenticator {
+    private static final Logger logger = LoggerFactory.getLogger(AbstractAuthenticator.class);
+
+    @Autowired
+    private UsersService userService;
+    @Autowired
+    private SessionService sessionService;
+
+    /**
+     * user login and return user in db
+     *
+     * @param userId user identity field
+     * @param password user login password
+     * @param extra extra user login field
+     * @return user object in databse
+     */
+    public abstract User login(String userId, String password, String extra);
+
+    @Override
+    public Result<Map<String, String>> authenticate(String userId, String password, String extra) {
+        Result<Map<String, String>> result = new Result<>();
+        User user = login(userId, password, extra);
+        if (user == null) {
+            result.setCode(Status.USER_NAME_PASSWD_ERROR.getCode());
+            result.setMsg(Status.USER_NAME_PASSWD_ERROR.getMsg());
+            return result;
+        }
+
+        // check user state
+        if (user.getState() == Flag.NO.ordinal()) {
+            result.setCode(Status.USER_DISABLED.getCode());
+            result.setMsg(Status.USER_DISABLED.getMsg());
+            return result;
+        }
+
+        // create session
+        String sessionId = sessionService.createSession(user, extra);
+        if (sessionId == null) {
+            result.setCode(Status.LOGIN_SESSION_FAILED.getCode());
+            result.setMsg(Status.LOGIN_SESSION_FAILED.getMsg());
+            return result;
+        }
+        logger.info("sessionId : {}", sessionId);
+        result.setData(Collections.singletonMap(Constants.SESSION_ID, sessionId));
+        result.setCode(Status.SUCCESS.getCode());
+        result.setMsg(Status.LOGIN_SUCCESS.getMsg());
+        return result;
+    }
+
+    @Override
+    public User getAuthUser(HttpServletRequest request) {
+        Session session = sessionService.getSession(request);
+        if (session == null) {
+            logger.info("session info is null ");
+            return null;
+        }
+        //get user object from session
+        return userService.queryUser(session.getUserId());
+    }
+
+}

+ 45 - 0
spring-ldap/impl/ldap/LdapAuthenticator.java

@@ -0,0 +1,45 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class LdapAuthenticator extends AbstractAuthenticator {
+    @Autowired
+    private UsersService usersService;
+    @Autowired
+    LdapService ldapService;
+
+    @Override
+    public User login(String userId, String password, String extra) {
+        User user = null;
+        String ldapEmail = ldapService.ldapLogin(userId, password);
+        if (ldapEmail != null) {
+            //check if user exist
+            user = usersService.getUserByUserName(userId);
+            if (user == null) {
+                user = usersService.createUser(ldapService.getUserType(userId), userId, ldapEmail);
+            }
+        }
+        return user;
+    }
+}

+ 133 - 0
spring-ldap/impl/ldap/LdapService.java

@@ -0,0 +1,133 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.ldap;
+
+import org.apache.dolphinscheduler.common.enums.UserType;
+
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Component
+@Configuration
+public class LdapService {
+    private static final Logger logger = LoggerFactory.getLogger(LdapService.class);
+
+    @Value("${security.authentication.ldap.user.admin:null}")
+    private String adminUserId;
+
+    @Value("${ldap.urls:null}")
+    private String ldapUrls;
+
+    @Value("${ldap.base.dn:null}")
+    private String ldapBaseDn;
+
+    @Value("${ldap.username:null}")
+    private String ldapSecurityPrincipal;
+
+    @Value("${ldap.password:null}")
+    private String ldapPrincipalPassword;
+
+    @Value("${ldap.user.identity.attribute:null}")
+    private String ldapUserIdentifyingAttribute;
+
+    @Value("${ldap.user.email.attribute:null}")
+    private String ldapEmailAttribute;
+
+    /***
+     * get user type by configured admin userId
+     * @param userId login userId
+     * @return user type
+     */
+    public UserType getUserType(String userId) {
+        return adminUserId.equalsIgnoreCase(userId) ? UserType.ADMIN_USER : UserType.GENERAL_USER;
+    }
+
+    /**
+     * login by userId and return user email
+     *
+     * @param userId user identity id
+     * @param userPwd user login password
+     * @return user email
+     */
+    public String ldapLogin(String userId, String userPwd) {
+        Properties searchEnv = getManagerLdapEnv();
+        try {
+            //Connect to the LDAP server and Authenticate with a service user of whom we know the DN and credentials
+            LdapContext ctx = new InitialLdapContext(searchEnv, null);
+            SearchControls sc = new SearchControls();
+            sc.setReturningAttributes(new String[]{ldapEmailAttribute});
+            sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            String searchFilter = String.format("(%s=%s)", ldapUserIdentifyingAttribute, userId);
+            //Search for the user you want to authenticate, search him with some attribute
+            NamingEnumeration<SearchResult> results = ctx.search(ldapBaseDn, searchFilter, sc);
+            if (results.hasMore()) {
+                // get the users DN (distinguishedName) from the result
+                SearchResult result = results.next();
+                NamingEnumeration attrs = result.getAttributes().getAll();
+                while (attrs.hasMore()) {
+                    //Open another connection to the LDAP server with the found DN and the password
+                    searchEnv.put(Context.SECURITY_PRINCIPAL, result.getNameInNamespace());
+                    searchEnv.put(Context.SECURITY_CREDENTIALS, userPwd);
+                    try {
+                        new InitialDirContext(searchEnv);
+                    } catch (Exception e) {
+                        logger.warn("invalid ldap credentials or ldap search error", e);
+                        return null;
+                    }
+                    Attribute attr = (Attribute) attrs.next();
+                    if (attr.getID().equals(ldapEmailAttribute)) {
+                        return (String) attr.get();
+                    }
+                }
+            }
+        } catch (NamingException e) {
+            logger.error("ldap search error", e);
+            return null;
+        }
+        return null;
+    }
+
+    /***
+     * get ldap env fot ldap server search
+     * @return Properties
+     */
+    Properties getManagerLdapEnv() {
+        Properties env = new Properties();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL, ldapSecurityPrincipal);
+        env.put(Context.SECURITY_CREDENTIALS, ldapPrincipalPassword);
+        env.put(Context.PROVIDER_URL, ldapUrls);
+        return env;
+    }
+}

+ 34 - 0
spring-ldap/impl/pwd/PasswordAuthenticator.java

@@ -0,0 +1,34 @@
+/*
+ * 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.dolphinscheduler.api.security.impl.pwd;
+
+import org.apache.dolphinscheduler.api.security.impl.AbstractAuthenticator;
+import org.apache.dolphinscheduler.api.service.UsersService;
+import org.apache.dolphinscheduler.dao.entity.User;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class PasswordAuthenticator extends AbstractAuthenticator {
+    @Autowired
+    private UsersService userService;
+
+    @Override
+    public User login(String userId, String password, String extra) {
+        return userService.queryUser(userId, password);
+    }
+}

+ 22 - 0
spring-ldap/spring-ldap-test.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:ldap="http://www.springframework.org/schema/ldap"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           https://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/ldap
+                           https://www.springframework.org/schema/ldap/spring-ldap.xsd">
+
+    <ldap:context-source
+            url="ldap://localhost:10389"
+            base="ou=yiidata,dc=yiidata,dc=com"
+            username="uid=admin,ou=system"
+            password="secret" />
+
+    <ldap:ldap-template id="ldapTemplate" />
+
+    <bean id="personRepo" class="com.primeton.poctag.service.ldap.impl.PersonRepoImpl">
+        <property name="ldapTemplate" ref="ldapTemplate" />
+    </bean>
+</beans>
+

+ 18 - 3
sql-template-file/HibernateStatementCompareField.java

@@ -10,7 +10,7 @@ public class HibernateStatementCompareField extends HibernateStatementField
   private String compare;
   private String compareProperty;
   private String compareValue;
-  public static final String AVAILABLE_COMPARE = "eq,ne,gt,ge,lt,le";
+  public static final String AVAILABLE_COMPARE = "eq,ne,gt,ge,lt,le,isnull,isnotnull,isempty,isnotempty";
 
   public String getCompare()
   {
@@ -49,7 +49,7 @@ public class HibernateStatementCompareField extends HibernateStatementField
 
   private static int getCompareOperateCode(String compare)
   {
-    String[] ac = "eq,ne,gt,ge,lt,le".split(",");
+    String[] ac = AVAILABLE_COMPARE.split(",");
     for (int i = 0; i < ac.length; i++) {
       if (ac[i].equalsIgnoreCase(compare)) {
         return i;
@@ -66,6 +66,21 @@ public class HibernateStatementCompareField extends HibernateStatementField
   private static boolean match(String compare, Object value, String compareValue)
     throws NumberFormatException
   {
+    if("null".equals(compareValue) && compare.equals("eq")) {
+      compare = "isnull";
+    } else if("null".equals(compareValue) && compare.equals("ne")) {
+      compare = "isnotnull";
+    }
+    switch (compare) {
+      case "isnull":
+        return value == null;
+      case "isnotnull":
+        return value != null;
+      case "isempty":
+        return StringUtils.isEmpty("" + (value == null ? "" : String.valueOf(value)));
+      case "isnotempty":
+        return StringUtils.isNotEmpty(""+ (value == null ? "" : String.valueOf(value)));
+    }
     if (value == null) {
       return false;
     }
@@ -113,7 +128,7 @@ public class HibernateStatementCompareField extends HibernateStatementField
   @Override
   public StringBuffer getRawText(Object vo)
   {
-    return getText(vo, true, new AtomicInteger(-1));
+    return getText(vo, true, new AtomicInteger(0));
   }
 
 

+ 1 - 1
sql-template-file/HibernateStatementDynamicField.java

@@ -42,7 +42,7 @@ public class HibernateStatementDynamicField extends HibernateStatementField
 
   public StringBuffer getRawText(Object vo)
   {
-    return getText(vo, true, new AtomicInteger(-1));
+    return getText(vo, true, new AtomicInteger(0));
   }
 
 

+ 9 - 5
sql-template-file/HibernateStatementSessionFactoryBean.java

@@ -102,8 +102,12 @@ public class HibernateStatementSessionFactoryBean implements Serializable {
 		this.ready = false;
 		this.statements.clear();
 		for (int i = 0; i < this.statementLocations.length; i++) {
-			config(this.statementLocations[i].getInputStream());
-			log.info("read xml sql template file: {}", statementLocations[i].getURI());
+			try(final InputStream inputStream = this.statementLocations[i].getInputStream();) {
+				config(inputStream);
+				log.info("read xml sql template file: {}", statementLocations[i].getURI());
+			} catch (Exception e) {
+				log.error("parse sql xml error.", e);
+			}
 		}
 		if ((isMerge()) && (this.parent != null)) {
 			doMerge();
@@ -205,13 +209,13 @@ public class HibernateStatementSessionFactoryBean implements Serializable {
 			throw new HibernateStatementException(
 					"Attribute \"compareProperty\" of compare-field is required!");
 		}
-		if ((compareValue == null) || ("".equals(compareValue))) {
+		if ((compareValue == null)) {
 			throw new HibernateStatementException(
 					"Attribute \"compareValue\" of compare-field is required!");
 		}
 		if (!HibernateStatementCompareField.isAvailableCompare(compare)) {
 			throw new HibernateStatementException(
-					"Attribute \"compare\" of compare-field is unavailable, the available value is (eq,ne,gt,ge,lt,le)!");
+					"Attribute \"compare\" of compare-field is unavailable, the available value is (" + HibernateStatementCompareField.AVAILABLE_COMPARE + ")!");
 		}
 
 		HibernateStatementCompareField f = new HibernateStatementCompareField(
@@ -255,7 +259,7 @@ public class HibernateStatementSessionFactoryBean implements Serializable {
 			return null;
 		}
 		StringBuffer sb = new StringBuffer();
-		AtomicInteger index = new AtomicInteger(0);
+		AtomicInteger index = new AtomicInteger(1);
 		try {
 			for (int i = 0; i < fileds.size(); i++) {
 				StringBuffer s = ((HibernateStatementField) fileds.get(i)).getText(vo, index);

+ 0 - 9
sql-template-file/HibernateStatementTextField.java

@@ -99,13 +99,4 @@ public class HibernateStatementTextField extends HibernateStatementField
     }
     return sb;
   }
-
-  public static void main(String[] args) {
-    HibernateStatementTextField textField = new HibernateStatementTextField();
-    textField.setValue("select * from and tt.LINK_ID in ($roleIds$), ($roleIds$)");
-    final HashMap<Object, Object> vo = new HashMap<>();
-    vo.put("roleIds", "ABC");
-    System.out.println(textField.getText(vo, new AtomicInteger(0)));
-    System.out.println(textField.getRawText(vo));
-  }
 }

+ 1 - 1
swagger-support/SwaggerConfig.java

@@ -32,7 +32,7 @@ import java.util.Optional;
  */
 @Configuration //必须存在
 @EnableSwagger2 //必须存在
-@ConditionalOnProperty(name = "swagger.enable", havingValue = "true", matchIfMissing = true)
+@ConditionalOnProperty(name = "bftconsole.swagger.enable", havingValue = "true", matchIfMissing = true)
 public class SwaggerConfig {
 
     @Value("${swagger.group}")