parent
9beda40926
commit
510b397ca7
21 changed files with 587 additions and 18 deletions
@ -0,0 +1,13 @@ |
||||
package com.xxl.job.dao; |
||||
|
||||
import com.xxl.job.dao.model.XxlJobInfo; |
||||
|
||||
/** |
||||
* job log for glue |
||||
* @author xuxueli 2016-5-19 18:04:56 |
||||
*/ |
||||
public interface IXxlJobInfoDao { |
||||
|
||||
public XxlJobInfo load(String jobGroup, String jobName); |
||||
|
||||
} |
@ -0,0 +1,32 @@ |
||||
package com.xxl.job.dao.impl; |
||||
|
||||
import java.util.HashMap; |
||||
|
||||
import javax.annotation.Resource; |
||||
|
||||
import org.mybatis.spring.SqlSessionTemplate; |
||||
import org.springframework.stereotype.Repository; |
||||
|
||||
import com.xxl.job.dao.IXxlJobInfoDao; |
||||
import com.xxl.job.dao.model.XxlJobInfo; |
||||
|
||||
/** |
||||
* job log for glue |
||||
* @author xuxueli 2016-5-19 18:17:52 |
||||
*/ |
||||
@Repository |
||||
public class XxlJobInfoDaoImpl implements IXxlJobInfoDao { |
||||
|
||||
@Resource |
||||
public SqlSessionTemplate sqlSessionTemplate; |
||||
|
||||
@Override |
||||
public XxlJobInfo load(String jobGroup, String jobName) { |
||||
HashMap<String, Object> params = new HashMap<String, Object>(); |
||||
params.put("jobGroup", jobGroup); |
||||
params.put("jobName", jobName); |
||||
return sqlSessionTemplate.selectOne("XxlJobInfoMapper.load", params); |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,38 @@ |
||||
package com.xxl.job.dao.model; |
||||
|
||||
/** |
||||
* xxl-job info |
||||
* @author xuxueli 2016-5-19 17:57:46 |
||||
*/ |
||||
public class XxlJobInfo { |
||||
|
||||
private String jobGroup; |
||||
private String jobName; |
||||
|
||||
private String glueSource; |
||||
|
||||
public String getJobGroup() { |
||||
return jobGroup; |
||||
} |
||||
|
||||
public void setJobGroup(String jobGroup) { |
||||
this.jobGroup = jobGroup; |
||||
} |
||||
|
||||
public String getJobName() { |
||||
return jobName; |
||||
} |
||||
|
||||
public void setJobName(String jobName) { |
||||
this.jobName = jobName; |
||||
} |
||||
|
||||
public String getGlueSource() { |
||||
return glueSource; |
||||
} |
||||
|
||||
public void setGlueSource(String glueSource) { |
||||
this.glueSource = glueSource; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
package com.xxl.job.service.loader; |
||||
|
||||
import javax.annotation.Resource; |
||||
|
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import com.xxl.job.client.glue.loader.GlueLoader; |
||||
import com.xxl.job.dao.IXxlJobInfoDao; |
||||
import com.xxl.job.dao.model.XxlJobInfo; |
||||
|
||||
@Service("dbGlueLoader") |
||||
public class DbGlueLoader implements GlueLoader { |
||||
|
||||
@Resource |
||||
private IXxlJobInfoDao xxlJobInfoDao; |
||||
|
||||
@Override |
||||
public String load(String job_group, String job_name) { |
||||
XxlJobInfo glue = xxlJobInfoDao.load(job_group, job_name); |
||||
return glue!=null?glue.getGlueSource():null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,50 @@ |
||||
<?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:context="http://www.springframework.org/schema/context" |
||||
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" |
||||
xsi:schemaLocation=" |
||||
http://www.springframework.org/schema/beans |
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd |
||||
http://www.springframework.org/schema/context |
||||
http://www.springframework.org/schema/context/spring-context-3.0.xsd |
||||
http://www.springframework.org/schema/aop |
||||
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd |
||||
http://www.springframework.org/schema/tx |
||||
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> |
||||
|
||||
<context:component-scan base-package="com.xxl.job.service, com.xxl.job.dao" /> |
||||
|
||||
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
||||
<property name="fileEncoding" value="utf-8" /> |
||||
<property name="locations"> |
||||
<list> |
||||
<value>classpath*:jdbc.properties</value> |
||||
</list> |
||||
</property> |
||||
</bean> |
||||
|
||||
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> |
||||
<property name="driverClass" value="${c3p0.driverClass}" /> |
||||
<property name="jdbcUrl" value="${c3p0.url}" /> |
||||
<property name="user" value="${c3p0.user}" /> |
||||
<property name="password" value="${c3p0.password}" /> |
||||
<property name="initialPoolSize" value="3" /> |
||||
<property name="minPoolSize" value="2" /> |
||||
<property name="maxPoolSize" value="10" /> |
||||
<property name="maxIdleTime" value="60" /> |
||||
<property name="acquireRetryDelay" value="1000" /> |
||||
<property name="acquireRetryAttempts" value="10" /> |
||||
<property name="preferredTestQuery" value="SELECT 1" /> |
||||
</bean> |
||||
|
||||
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> |
||||
<property name="dataSource" ref="dataSource" /> |
||||
<property name="mapperLocations" value="classpath*:mybatis-mapper/*.xml"/> |
||||
</bean> |
||||
|
||||
<!-- scope must be "prototype" when junit --> |
||||
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype"> |
||||
<constructor-arg index="0" ref="sqlSessionFactory" /> |
||||
</bean> |
||||
|
||||
</beans> |
@ -0,0 +1,4 @@ |
||||
c3p0.driverClass=com.mysql.jdbc.Driver |
||||
c3p0.url=jdbc:mysql://localhost:3306/xxl-job?Unicode=true&characterEncoding=UTF-8 |
||||
c3p0.user=root |
||||
c3p0.password=root_pwd |
@ -0,0 +1,26 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="XxlJobInfoMapper"> |
||||
|
||||
<resultMap id="XxlJobInfo" type="com.xxl.job.dao.model.XxlJobInfo" > |
||||
<result column="job_group" property="jobGroup" /> |
||||
<result column="job_name" property="jobName" /> |
||||
<result column="glue_source" property="glueSource" /> |
||||
</resultMap> |
||||
|
||||
<sql id="Base_Column_List"> |
||||
t.job_group, |
||||
t.job_name, |
||||
t.glue_source |
||||
</sql> |
||||
|
||||
<select id="load" parameterType="java.util.HashMap" resultMap="XxlJobInfo"> |
||||
SELECT <include refid="Base_Column_List" /> |
||||
FROM xxl_job_qrtz_trigger_info AS t |
||||
WHERE t.job_group = #{jobGroup} |
||||
AND t.job_name = #{jobName} |
||||
</select> |
||||
|
||||
|
||||
</mapper> |
@ -0,0 +1,187 @@ |
||||
package com.xxl.job.client.glue; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.Modifier; |
||||
|
||||
import javax.annotation.Resource; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
|
||||
import com.xxl.job.client.glue.cache.LocalCache; |
||||
import com.xxl.job.client.glue.loader.GlueLoader; |
||||
import com.xxl.job.client.handler.IJobHandler; |
||||
import com.xxl.job.client.handler.IJobHandler.JobHandleStatus; |
||||
|
||||
import groovy.lang.GroovyClassLoader; |
||||
|
||||
/** |
||||
* glue factory, product class/object by name |
||||
* @author xuxueli 2016-1-2 20:02:27 |
||||
*/ |
||||
public class GlueFactory implements ApplicationContextAware { |
||||
private static Logger logger = LoggerFactory.getLogger(GlueFactory.class); |
||||
|
||||
/** |
||||
* groovy class loader |
||||
*/ |
||||
private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); |
||||
|
||||
/** |
||||
* glue cache timeout / second |
||||
*/ |
||||
private long cacheTimeout = 5000; |
||||
public void setCacheTimeout(long cacheTimeout) { |
||||
this.cacheTimeout = cacheTimeout; |
||||
} |
||||
|
||||
/** |
||||
* code source loader |
||||
*/ |
||||
private GlueLoader glueLoader; |
||||
public void setGlueLoader(GlueLoader glueLoader) { |
||||
this.glueLoader = glueLoader; |
||||
} |
||||
|
||||
// ----------------------------- spring support -----------------------------
|
||||
private static ApplicationContext applicationContext; |
||||
private static GlueFactory glueFactory; |
||||
|
||||
@Override |
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||
GlueFactory.applicationContext = applicationContext; |
||||
GlueFactory.glueFactory = (GlueFactory) applicationContext.getBean("glueFactory"); |
||||
} |
||||
|
||||
/** |
||||
* inject service of spring |
||||
* @param instance |
||||
*/ |
||||
public void injectService(Object instance){ |
||||
if (instance==null) { |
||||
return; |
||||
} |
||||
|
||||
Field[] fields = instance.getClass().getDeclaredFields(); |
||||
for (Field field : fields) { |
||||
if (Modifier.isStatic(field.getModifiers())) { |
||||
continue; |
||||
} |
||||
|
||||
Object fieldBean = null; |
||||
// with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired
|
||||
if (AnnotationUtils.getAnnotation(field, Resource.class) != null) { |
||||
try { |
||||
fieldBean = applicationContext.getBean(field.getName()); |
||||
} catch (Exception e) { |
||||
} |
||||
if (fieldBean==null ) { |
||||
fieldBean = applicationContext.getBean(field.getType()); |
||||
} |
||||
} else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) { |
||||
fieldBean = applicationContext.getBean(field.getType()); |
||||
} |
||||
|
||||
if (fieldBean!=null) { |
||||
field.setAccessible(true); |
||||
try { |
||||
field.set(instance, fieldBean); |
||||
} catch (IllegalArgumentException e) { |
||||
e.printStackTrace(); |
||||
} catch (IllegalAccessException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// ----------------------------- load instance -----------------------------
|
||||
|
||||
// load class,
|
||||
public static String generateClassCacheKey(String job_group, String job_name){ |
||||
return job_group.concat("_").concat(job_name).concat("_class"); |
||||
} |
||||
public Class<?> loadClass(String job_group, String job_name) throws Exception{ |
||||
if (job_group==null || job_group.trim().length()==0 || job_name==null || job_name.trim().length()==0) { |
||||
return null; |
||||
} |
||||
String cacheClassKey = generateClassCacheKey(job_group, job_name); |
||||
Object cacheClass = LocalCache.getInstance().get(cacheClassKey); |
||||
if (cacheClass != null) { |
||||
return (Class<?>) cacheClass; |
||||
} |
||||
String codeSource = glueLoader.load(job_group, job_name); |
||||
if (codeSource!=null && codeSource.trim().length()>0) { |
||||
Class<?> clazz = groovyClassLoader.parseClass(codeSource); |
||||
if (clazz!=null) { |
||||
LocalCache.getInstance().set(cacheClassKey, clazz, cacheTimeout); |
||||
logger.info(">>>>>>>>>>>> xxl-glue, fresh class, cacheClassKey:{}", cacheClassKey); |
||||
return clazz; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
// load new instance, prototype
|
||||
public IJobHandler loadNewInstance(String job_group, String job_name) throws Exception{ |
||||
if (job_group==null || job_group.trim().length()==0 || job_name==null || job_name.trim().length()==0) { |
||||
return null; |
||||
} |
||||
Class<?> clazz = loadClass(job_group, job_name); |
||||
if (clazz!=null) { |
||||
Object instance = clazz.newInstance(); |
||||
if (instance!=null) { |
||||
if (!(instance instanceof IJobHandler)) { |
||||
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, " |
||||
+ "cannot convert from instance["+ instance.getClass() +"] to IJobHandler"); |
||||
} |
||||
|
||||
this.injectService(instance); |
||||
return (IJobHandler) instance; |
||||
} |
||||
} |
||||
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, instance is null"); |
||||
} |
||||
|
||||
// // load instance, singleton
|
||||
public static String generateInstanceCacheKey(String job_group, String job_name){ |
||||
return job_group.concat("_").concat(job_name).concat("_instance"); |
||||
} |
||||
public IJobHandler loadInstance(String job_group, String job_name) throws Exception{ |
||||
if (job_group==null || job_group.trim().length()==0 || job_name==null || job_name.trim().length()==0) { |
||||
return null; |
||||
} |
||||
String cacheInstanceKey = generateInstanceCacheKey(job_group, job_name); |
||||
Object cacheInstance = LocalCache.getInstance().get(cacheInstanceKey); |
||||
if (cacheInstance!=null) { |
||||
if (!(cacheInstance instanceof IJobHandler)) { |
||||
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadInstance error, " |
||||
+ "cannot convert from cacheClass["+ cacheInstance.getClass() +"] to IJobHandler"); |
||||
} |
||||
return (IJobHandler) cacheInstance; |
||||
} |
||||
Object instance = loadNewInstance(job_group, job_name); |
||||
if (instance!=null) { |
||||
if (!(instance instanceof IJobHandler)) { |
||||
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadInstance error, " |
||||
+ "cannot convert from instance["+ instance.getClass() +"] to IJobHandler"); |
||||
} |
||||
|
||||
LocalCache.getInstance().set(cacheInstanceKey, instance, cacheTimeout); |
||||
logger.info(">>>>>>>>>>>> xxl-glue, fresh instance, cacheInstanceKey:{}", cacheInstanceKey); |
||||
return (IJobHandler) instance; |
||||
} |
||||
throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadInstance error, instance is null"); |
||||
} |
||||
|
||||
// ----------------------------- util -----------------------------
|
||||
public static JobHandleStatus glue(String job_group, String job_name, String... params) throws Exception{ |
||||
return GlueFactory.glueFactory.loadInstance(job_group, job_name).handle(params); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,17 @@ |
||||
package com.xxl.job.client.glue.cache; |
||||
|
||||
/** |
||||
* chche interface
|
||||
* @author xuxueli 2016-1-8 15:57:27 |
||||
*/ |
||||
public interface ICache { |
||||
|
||||
public boolean set(String key, Object value); |
||||
|
||||
public boolean set(String key, Object value, long timeout); |
||||
|
||||
public Object get(String key); |
||||
|
||||
public boolean remove(String key); |
||||
|
||||
} |
@ -0,0 +1,71 @@ |
||||
package com.xxl.job.client.glue.cache; |
||||
|
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
/** |
||||
* local interface
|
||||
* @author Administrator |
||||
*/ |
||||
public class LocalCache implements ICache{ |
||||
|
||||
private static final LocalCache instance = new LocalCache(); |
||||
public static LocalCache getInstance(){ |
||||
return instance; |
||||
} |
||||
|
||||
private static final ConcurrentHashMap<String, Object> cacheMap = new ConcurrentHashMap<String, Object>(); |
||||
private static final long CACHE_TIMEOUT = 5000; |
||||
|
||||
private static String makeTimKey(String key){ |
||||
return key.concat("_tim"); |
||||
} |
||||
private static String makeDataKey(String key){ |
||||
return key.concat("_data"); |
||||
} |
||||
|
||||
@Override |
||||
public boolean set(String key, Object value) { |
||||
cacheMap.put(makeTimKey(key), System.currentTimeMillis() + CACHE_TIMEOUT); |
||||
cacheMap.put(makeDataKey(key), value); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean set(String key, Object value, long timeout) { |
||||
cacheMap.put(makeTimKey(key), System.currentTimeMillis() + timeout); |
||||
cacheMap.put(makeDataKey(key), value); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Object get(String key) { |
||||
Object tim = cacheMap.get(makeTimKey(key)); |
||||
if (tim != null && System.currentTimeMillis() < Long.parseLong(tim.toString())) { |
||||
return cacheMap.get(makeDataKey(key)); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public boolean remove(String key) { |
||||
cacheMap.remove(makeTimKey(key)); |
||||
cacheMap.remove(makeDataKey(key)); |
||||
return true; |
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
String key = "key01"; |
||||
System.out.println(LocalCache.getInstance().get(key)); |
||||
|
||||
LocalCache.getInstance().set(key, "v1"); |
||||
System.out.println(LocalCache.getInstance().get(key)); |
||||
|
||||
LocalCache.getInstance().set(key, "v2"); |
||||
System.out.println(LocalCache.getInstance().get(key)); |
||||
|
||||
LocalCache.getInstance().remove(key); |
||||
System.out.println(LocalCache.getInstance().get(key)); |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,16 @@ |
||||
package com.xxl.job.client.glue.loader; |
||||
|
||||
/** |
||||
* code source loader |
||||
* @author xuxueli 2016-1-2 20:01:39 |
||||
*/ |
||||
public interface GlueLoader { |
||||
|
||||
/** |
||||
* load code source by name, ensure every load is the latest. |
||||
* @param name |
||||
* @return |
||||
*/ |
||||
public String load(String job_group, String job_name); |
||||
|
||||
} |
@ -1,4 +1,4 @@ |
||||
package com.xxl.job.client.handler; |
||||
package com.xxl.job.client.handler.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
@ -0,0 +1,24 @@ |
||||
package com.xxl.job.client.handler.impl; |
||||
|
||||
import com.xxl.job.client.glue.GlueFactory; |
||||
import com.xxl.job.client.handler.IJobHandler; |
||||
|
||||
/** |
||||
* glue job handler |
||||
* @author xuxueli 2016-5-19 21:05:45 |
||||
*/ |
||||
public class GlueJobHandler extends IJobHandler { |
||||
|
||||
private String job_group; |
||||
private String job_name; |
||||
public GlueJobHandler(String job_group, String job_name) { |
||||
this.job_group = job_group; |
||||
this.job_name = job_name; |
||||
} |
||||
|
||||
@Override |
||||
public JobHandleStatus handle(String... params) throws Exception { |
||||
return GlueFactory.glue(job_group, job_name, params); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue