
开源爱好者,喜欢钻研新技术。Activiti权威指南一书作者。
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
摘要:从本文开始我们会详细讲解springboot中一系列的事件监听器的使用以及内部实现原理,本文暂且讲解最简单的一个监听器,那就是文件编码监听器-FileEncodingApplicationListener。 FileEncodingApplicationListener类相关源码如下: public class FileEncodingApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { private static final Log logger = LogFactory.getLog(FileEncodingApplicationListener.class); public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.containsProperty("spring.mandatory-file-encoding")) { return; } String encoding = System.getProperty("file.encoding"); String desired = environment.getProperty("spring.mandatory-file-encoding"); if (encoding != null && !desired.equalsIgnoreCase(encoding)) { ... } } } FileEncodingApplicationListener是一个文件编码监听器,它实现了Ordered接口,通过getOrder方法我们可以看出它的执行优先级是比较低的(getOrder方法的返回值越小,则执行优先级越高)。FileEncodingApplicationListener负责监听ApplicationEnvironmentPreparedEvent事件。ApplicationEnvironmentPreparedEvent:环境事先准备,spring boot中的环境已经准备ok 可以通过ApplicationEnvironmentPreparedEvent获取到SpringApplication、ConfigurableEnvironment等等信息, 可以通过ConfigurableEnvironment实例对象来修改以及获取默认的环境信息。 接下来我们重点梳理一下onApplicationEvent方法的处理逻辑。 1ã通过event获取环境ConfigurableEnvironment 。 2ã通过环境来获取spring.mandatory-file-encoding变量(所有的配置文件最终都被springboot解析存储到环境中),如果不存在该变量,则直接返回。spring.mandatory-file-encoding变量默认就没有设置,因此我们如果没有设置该变量的值,就不会执行下文的逻辑。 3ã获取系统变量file.encoding。获取spring.mandatory-file-encoding变量。 4ã如果spring.mandatory-file-encoding的值(忽略大小写)与file.encoding的值不相等,则直接报错,直接不让程序运行。比如我们设置了spring.mandatory-file-encoding=utf-8,file.encoding的值为GBK则报错;如果spring.mandatory-file-encoding=uTF-8,file.encoding的值为utf-8就不会报错。因为比较两者值的时候已经忽略大小写了。 关于spring.mandatory-file-encoding属性的配置步骤如下: 在项目的根目录中创建application.properties文件,该文件的层级结构如下图所示: application.properties文件的内容如下: spring.mandatory-file-encoding=GBK 至此,FileEncodingApplicationListener类的相关实现以及原理已经讲解完毕。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:spring boot提供了一系列的监听器,方便我们开发人员使用和扩展。 本文咱们详细讲解一下spring boot中的监听器。 spring boot中支持的事件类型定在org.springframework.boot.context.event包中,目前支持的事件类型有如下6种: ApplicationFailedEvent ApplicationPreparedEvent ApplicationReadyEvent ApplicationStartedEvent(Springboot2.x版本已修改为ApplicationStartingEvent) SpringApplicationEvent ApplicationEnvironmentPreparedEvent 1.1. 监听器的使用 第一:首先定义一个自己使用的监听器类并实现ApplicationListener接口。 第二:通过SpringApplication类中的addListeners方法将自定义的监听器注册进去。 1.1.1. ApplicationFailedEvent ApplicationFailedEvent:该事件为spring boot启动失败时的操作。 /** * spring boot 启动的时候出现异常事件 * @author www.shareniu.com * */ public class ShareniuApplicationFailedEventListener implements ApplicationListener<ApplicationFailedEvent> { @Override public void onApplicationEvent(ApplicationFailedEvent event) { System.out.println("--------------:ShareniuApplicationFailedEventListener"); Throwable exception = event.getException(); System.out.println(exception); } } 可以通过ApplicationFailedEvent 获取Throwable实例对象获取异常信息并处理。 1.1.2. ApplicationPreparedEvent ApplicationPreparedEvent:上下文准备事件。 上下文context已经准备完毕 ,可以通过ApplicationPreparedEvent获取到ConfigurableApplicationContext实例对象。ConfigurableApplicationContext类继承ApplicationContext类,但需要注意这个时候spring容器中的bean还没有被完全的加载,因此如果通过ConfigurableApplicationContext获取bean会报错的。比如报错: Exception in thread "main" java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@69b0fd6f has not been refreshed yet 获取到上下文之后,可以将其注入到其他类中,毕竟ConfigurableApplicationContext为引用类型。 public class ShareniuApplicationPreparedEventListener implements ApplicationListener<ApplicationPreparedEvent> { @Override public void onApplicationEvent(ApplicationPreparedEvent event) { System.out.println("###############"+"ShareniuApplicationPreparedEventListener"); ConfigurableApplicationContext applicationContext = event.getApplicationContext(); //如果执行下面代码则报错 //ShareniuDemo shareniuDemo = applicationContext.getBean(ShareniuDemo.class); //System.out.println(shareniuDemo); } } 1.1.3. ApplicationReadyEvent ApplicationReadyEvent:上下文已经准备ok。 这个时候就可以通过ApplicationReadyEvent获取ConfigurableApplicationContext,然后通过ConfigurableApplicationContext 获取bean的信息。 public class ShareniuApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent> { @Override public void onApplicationEvent(ApplicationReadyEvent event) { System.out.println("--------------------:ShareniuApplicationReadyEventListener"); ConfigurableApplicationContext applicationContext = event.getApplicationContext(); //ShareniuDemo可以根基自身情况进行测试 ShareniuDemo shareniuDemo = applicationContext.getBean(ShareniuDemo.class); } } 1.1.4. ApplicationStartedEvent ApplicationStartedEvent:spring boot 启动监听类。该类在SpringBoot2.x版本中已经废弃,修改为了最新的类,类名是ApplicationStartingEvent。这个事件是第一个产生的。 可以在SpringApplication启动之前做一些手脚,比如修改SpringApplication实例对象中的属性值。 public class ShareniuApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent>{ @Override public void onApplicationEvent(ApplicationStartedEvent event) { SpringApplication springApplication = event.getSpringApplication(); springApplication.setShowBanner(false); System.out.println("##############################ShareniuApplicationStartedEventListener"); } } 1.1.5. SpringApplicationEvent SpringApplicationEvent:获取SpringApplication public class ShareniuSpringApplicationEventListener implements ApplicationListener<SpringApplicationEvent> { @Override public void onApplicationEvent(SpringApplicationEvent event) { System.out.println("-----------------------:ShareniuSpringApplicationEventListener"); SpringApplication springApplication = event.getSpringApplication(); System.out.println("###############"+springApplication); } } 1.1.6. ApplicationEnvironmentPreparedEvent ApplicationEnvironmentPreparedEvent:环境事先准备,spring boot中的环境已经准备ok 可以通过ApplicationEnvironmentPreparedEvent获取到SpringApplication、ConfigurableEnvironment等等信息, 可以通过ConfigurableEnvironment实例对象来修改以及获取默认的环境信息。 public class ShasreniuApplicationEnvironmentPreparedEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>{ @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { System.out.println("###############"+"ShasreniuApplicationEnvironmentPreparedEventListener"); SpringApplication springApplication = event.getSpringApplication(); ConfigurableEnvironment environment = event.getEnvironment(); long timestamp = event.getTimestamp(); Object source = event.getSource(); System.out.println("########################"+springApplication); System.out.println("########################"+environment); System.out.println("########################"+timestamp); System.out.println("########################"+source); MutablePropertySources propertySources = environment.getPropertySources(); if (propertySources!=null) { Iterator<PropertySource<?>> iterator = propertySources.iterator(); while (iterator.hasNext()) { PropertySource<?> propertySource = (PropertySource<?>) iterator.next(); System.out.println("##############:propertySource"+propertySource); } } } } 1.2. 监听器注册 @RestController @SpringBootApplication() public class Application { @RequestMapping("/") String index() { return "xxxxxxxxxxxxx"; } public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Application.class); springApplication.addListeners(new ShareniuApplicationStartedEventListener()); springApplication.addListeners(new ShasreniuApplicationEnvironmentPreparedEventListener()); springApplication.addListeners(new ShareniuApplicationPreparedEventListener()); springApplication.addListeners(new ShareniuApplicationFailedEventListener()); springApplication.addListeners(new ShareniuApplicationReadyEventListener()); springApplication.addListeners(new ShareniuSpringApplicationEventListener()); springApplication.run(args); } } 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:事件驱动模型,也就是我们经常提到用到的观察者模式。当然也可以将其理解为发布-订阅模型。具体的实现要素有如下几个方面。 1、首先是一对多的关系,一是目标对象,多则是观察者对象。比如报社是一个,而订报者是多个。 2、当目标对象的行为发生变化的时候,多个观察者对象会级联触发并做出相应的处理。换言之,目标对象的行为发生变化的时候,只需要通知一下所有的观察者对象(订阅过的)即可。具体的各个观察者怎么去处理,使用什么方式去处理,并不是目标对象所需要考虑的范畴。也就是说目标与观察者的实现是低耦合。目标对象的职责就是去通知各个观察者,各个观察者的职责是具体做事情的。大家各司其职协调工作。 3、目标对象怎么能在自身状态发生变化的时候,去实时通知到各个观察者呢?无外乎就是如下的两种思路。 实现方案1: 所有需要通知的观察者去目标对象中注册登记,当目标对象需要通知的时候,查询登记列表中的所有观察者,然后一个个的下发。 实现方案2: 所有需要通知的观察者去目标对象中注册登记,登记的时候告诉目标对象自己需要监听的事件类型,只有是自己注册的事件变化时,才接受通知,否则目标对象的其他事件不要通知这个观察者。 上述的两个方案,方案1强调的是目标对象只要发生行为状态改变,所有的观察者都可以收到通知,并自行处理。方案2有点类似精准投递,比如观察者对象1只监听a事件,那么当目标对象触发b事件的时候不需要通知观察者对象1。两种方案各有优缺点,我个人倾向使用方案2。因为该方案可以根据不同的事件源去通知不同的观察者。 了解了上述的内容之后,接下来我们看一下Springboot中所使用的事件发布机制以及ApplicationListener。 1.1. SpringApplicationRunListener 我们一步到位,直接定位到SpringApplication类中的run方法,相关实现代码如下: public ConfigurableApplicationContext run(String... args) { ... SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); ... } 我们重点看一下getRunListeners方法的处理逻辑,该方法的实例代码如下: private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( pringApplicationRunListener.class, types, this, args)); } 首先看一下getSpringFactoriesInstances方法,代码如下: private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances);//order值越小,越靠前 return instances; } 上述的代码,前面的系列文章也详细讲解过,就是查询所有META-INF/spring.factories配置文件中key为org.springframework.boot.SpringApplicationRunListener的值,我们找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置文件,相关配置如下所示: # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener 因此上述代码执行完毕之后。会通过反射实例化EventPublishingRunListener类,该类的构造函数如下: private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } 首先,实例化SimpleApplicationEventMulticaster类,然后调用application对象中的getListeners()方法,并循环将该函数的返回值集合添加到initialMulticaster中。initialMulticaster我们可以将其理解为事件发布器。getListeners()方法中返回的集合在哪里初始化的呢?我们继续回到SpringApplication类的构造函数中。如下所示: private List<ApplicationListener<?>> listeners; public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ... setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); } public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<>(); this.listeners.addAll(listeners); } 在SpringApplication类的构造函数中,也就直接通过getSpringFactoriesInstances方法直接获取到META-INF/spring.factories配置文件中key为org.springframework.context.ApplicationListener的值,我们找一下spring-boot-2.0.0.M7.jar中的META-INF/spring.factories配置文件,相关配置如下所示: # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener 上述的13个监听类均是监听不同的事件进行处理的,我们也可以自定义一些监听器进行业务处理,添加方式如下所示: SpringApplication springApplication = new SpringApplication(DemoApplication.class); springApplication.addListeners(new ShareniuApplicationListener()); 通过上述代码我们可以看出,所有的事件监听器最终存储在SpringApplication类中的listeners集合中。 1.2. Springboot中事件的发布以及监听 接下来,我们来看一下Springboot中事件的发布以及监听。我们继续回归到listeners.starting()方法中。 public void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } 循环遍历所有的listeners,并依此调用listeners中的starting方法。 注意:listeners是SpringApplicationRunListener类型,并非是ApplicationListener类型,这点一定不要搞混淆了。SpringApplicationRunListener中持有所有的ApplicationListener类型监听器集合。EventPublishingRunListener类中的starting方法代码如下: public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); } 注意这里产生的事件是ApplicationStartingEvent类型,因此只有监听到ApplicationStartingEvent事件的监听器才可以观察到进而进行自己的处理。this.initialMulticaster.multicastEvent方法实现如下: public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } 在构造ApplicationStartedEvent时传给它的基类EventObject的protected不可序列化属性source。实例化ApplicationStartedEvent后instance.getClass()并包装为ResolvableType类型以保存类型信息,并将它和event作为参数传入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先获取ApplicationListener,使用getApplicationListeners方法,这个方法中抛开对listener做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了retrieveApplicationListeners中通过了supportsEvent验证的监听器集合,这里就体现出了ResolvableType的作用,它保存了类型的信息同时对泛型类型也支持。 在整个SpringBoot启动的过程中,会先后出产生如下的几个事件。 ApplicationStartingEvent-->>ApplicationEnvironmentPreparedEvent-->>ApplicationPreparedEvent ContextRefreshedEvent-->>ApplicationReadyEvent-->>ContextClosedEvent 后续的系列文章,我们对于核心的监听器一个个进行讲解,从而加深印象。
摘要:Springboot中PropertySource注解的使用一文中,详细讲解了PropertySource注解的使用,通过PropertySource注解去加载指定的资源文件、然后将加载的属性注入到指定的配置类,@value以及@ConfigurationProperties的使用。但是也遗留一个问题,PropertySource注解貌似是不支持多种环境的动态切换?这个问题该如何解决呢?我们需要从源码中看看他到底是否支持。 首先,我们开始回顾一下上节课说的PropertySource注解的使用,实例代码如下: 1 @PropertySource( name="jdbc-bainuo-dev.properties", 2 value={"classpath:config/jdbc-bainuo-dev.properties"},ignoreResourceNotFound=false,encoding="UTF-8") 我们使用了PropertySource注解中的参数有:name、value、ignoreResourceNotFound、encoding。其实PropertySource注解还有一个参数,那就是factory,该参数默认的值为PropertySourceFactory.class。 PropertySource注解的定义如下: 1 public @interface PropertySource { 2 String name() default ""; 3 String[] value(); 4 boolean ignoreResourceNotFound() default false; 5 String encoding() default ""; 6 Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; 7 } 我们不妨先看一下PropertySourceFactory接口是做什么的?该接口的核心定义代码如下: 1 public interface PropertySourceFactory { 2 PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException; 3 } 上述代码中,PropertySourceFactory接口仅仅提供了一个createPropertySource方法,该方法就是创建PropertySource实例对象的,关于PropertySource的架构可以参考前面的系列文章进行学习,既然是接口,那肯定有实现类吧?该接口的默认实现类为DefaultPropertySourceFactory,代码如下: 1 public class DefaultPropertySourceFactory implements PropertySourceFactory { 2 public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { 3 return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); 4 } 5 } 首先判断name参数值是否为空,如果不为空直接实例化ResourcePropertySource并将name参数值进行传递,否则直接实例化ResourcePropertySource。看到这个地方的处理,感觉也没什么神奇的地方,那么问题来了,我们思考如下三个问题:DefaultPropertySourceFactory 类什么时候被Spring框架调用呢?Name参数值是如何传递过来的呢?ResourcePropertySource实例化的时候做了什么呢?我们一个个的来看源码进行分析。 1.1. DefaultPropertySourceFactory 类什么时候被Spring框架调用呢 第一个问题:DefaultPropertySourceFactory 类什么时候被Spring框架调用呢?这个我们就需要看一下我们定义的PropertySource注解是如何被Spring框架解析的?经过我的一系列排查,我找到了。Spring框架开始解析PropertySource注解的方法位于ConfigurationClassParser类中,为了防止大量的跟踪源码跟踪丢失了,自己也糊涂了。我们直奔主题,看一下processPropertySource方法,如下所示: 1 private static final PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new DefaultPropertySourceFactory(); 2 private void processPropertySource(AnnotationAttributes propertySource) throws IOException { 3 String name = propertySource.getString("name"); 4 if (!StringUtils.hasLength(name)) { 5 name = null; 6 } 7 String encoding = propertySource.getString("encoding"); 8 if (!StringUtils.hasLength(encoding)) { 9 encoding = null; 10 } 11 String[] locations = propertySource.getStringArray("value"); 12 boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound"); 13 Class factoryClass = propertySource.getClass("factory"); 14 PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? 15 DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); 16 for (String location : locations) { 17 try { 18 String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); 19 Resource resource = this.resourceLoader.getResource(resolvedLocation); 20 addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); 21 } 22 catch (IllegalArgumentException ex) { 23 if (ignoreResourceNotFound) { 24 if (logger.isInfoEnabled()) { 25 logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); 26 } 27 } 28 else { 29 throw ex; 30 } 31 } 32 catch (IOException ex) { 33 } 34 } 上述代码的逻辑分为如下几个核心的步骤: 1. 开始解析name、encoding值。 2. 解析value(数组)以及ignoreResourceNotFound值。 3. 解析factory,如果该值没有配置,默认为PropertySourceFactory则直接实例化DefaultPropertySourceFactory类,否则开始实例化自定义的类。换言之factory的处理类我们是可以进行自定义的。BeanUtils.instantiateClass是Spring中比较常用的一个工具类,其内部就是通过反射手段实例化类,在这里我们就不一一讲解了。 4. 循环遍历所有的location值,进行如下的处理。 4.1.对location进行SPEL表达式的解析。比如当前的配置环境中有一个属性为app=shareniu,我们配置的location为${app}最终值为shareniu。通过这里的处理逻辑可以知道location支持多环境的切换以及表达式的配置。 4.2.使用资源加载器resourceLoader将resolvedLocation抽象为Resource。 4.3.调用addPropertySource属性进行处理。将指定的资源处理之后,添加到当前springboot运行的环境中,这个前面的章节也详细讲解过类似的,在这里就不详细说明了。注意:这里调用了DefaultPropertySourceFactory类中的createPropertySource方法了。 5.如果上述的任意步骤报错,则开始查找ignoreResourceNotFound的值,如果该值为treu,则忽略异常,否则直接报错。在这里我们可以看出ignoreResourceNotFound参数值的配置非常的重要。 1.2. ResourcePropertySource 我们看一下ResourcePropertySource类的构造函数,主要看一下没有name参数的构造函数,如下所示: 1 public ResourcePropertySource(EncodedResource resource) throws IOException { 2 super(getNameForResource(resource.getResource()),PropertiesLoaderUtils.loadProperties(resource)); 3 this.resourceName = null; 4 } 上述代码,我们重点关注一下name值的生成逻辑。也就是getNameForResource中的处理逻辑,如下所示: 1 private static String getNameForResource(Resource resource) { 2 String name = resource.getDescription(); 3 if (!StringUtils.hasText(name)) { 4 name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource); 5 } 6 return name; 7 } 通过resource中的getDescription方法获取name值。如果name值为空,则重新生成,否则直接返回。大家又兴起可以看看resource中各个子类的定义以及使用。 PropertiesLoaderUtils.loadProperties(resource)毋庸置疑就是加载指定的属性文件了。 1.3. PropertySource多环境配置以及表达式使用 在springboot中,可以通过设置spring.profiles.active属性,达到不同环境配置文件的动态切换。我们看一下这种方式如何使用,首先在application.properties增加如下的信息: spring.profiles.active=dev application.properties文件位置如下图所示: 然后,我们修改CustomerDataSourceConfig1类,这个类的配置以及启动类进行测试可以参考上一篇文章进行学习。 1 @PropertySource( name="jdbc-bainuo-dev.properties",value= {"classpath:config/jdbc-bainuo-$ {spring.profiles.active}.properties"},ignoreResourceNotFound=false,encoding="UTF-8") 2 public class CustomerDataSourceConfig1 { 3 } 这里注意value的值已经修改为了:"classpath:config/jdbc-bainuo-${spring.profiles.active}.properties"。 ${spring.profiles.active}表达式可以动态的进行配置,从而达到动态切换不同的配置文件了。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:本文重点讲解一下Spring中@PropertySource注解的使用,如何通过PropertySource注解加载指定的配置文件。以及PropertySource注解与@ConfigurationProperties两个注解的配合使用。 1.1. PropertySource注解加载指定的属性文件 Spring框架提供了PropertySource注解,目的是加载指定的属性文件,接下来我们看一下如何使用该注解。首先我们定义一个配置类,并在类中添加PropertySource注解,如下所示: @Component @PropertySource(value= {"classpath:config/jdbc-bainuo-dev.properties"},ignoreResourceNotFound=false,encoding="UTF-8",name="jdbc-bainuo-dev.properties",) public class CustomerDataSourceConfig1 { private String url; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public String toString() { return "CustomerDataSourceConfig{" + "url='" + url + '\'' + '}'; } } 上述的代码目的是加载classpath路径中config文件中的jdbc-bainuo-dev.properties。其中encoding 用于指定读取属性文件所使用的编码,我们通常使用的是UTF-8;ignoreResourceNotFound含义是当指定的配置文件不存在是否报错,默认是false;比如上文中指定的加载属性文件是jdbc-bainuo-dev.properties。如果该文件不存在,则ignoreResourceNotFound为true的时候,程序不会报错,如果ignoreResourceNotFound为false的时候,程序直接报错。实际项目开发中,最好设置ignoreResourceNotFound为false。该参数默认值为false。 value值是设置需要加载的属性文件,可以一次性加载多个。name的值我们设置的是jdbc-bainuo-dev.properties。这个值在Springboot的环境中必须是唯一的,如果不设置,则值为:“class path resource [config/jdbc-bainuo-dev.properties]“。 可能很多人比较纳闷,为什么是“class path resource [config/jdbc-bainuo-dev.properties]“呢?这个就涉及到了Spring中对资源文件的封装类Resource。上文我们配置的value值为"classpath:config/jdbc-bainuo-dev.properties",因此Spring发现是classpath开头的,因此最终使用的是Resource的子类ClassPathResource。如果是file开头的,则最终使用的类是FileSystemResource。 了解了上文所述的Resource类之后。我们再次明确一点,如果@PropertySource中如果没有设置name值,则name值的生成规则是:根据value值查找到最终封装的Resource子类,然后调用具体的Resource子类实例对象中的getDescription方法,getDescription方法的返回值为最终的name值。比如ClassPathResource类中的getDescription方法实现如下: public String getDescription() { StringBuilder builder = new StringBuilder("class path resource ["); String pathToUse = path; if (this.clazz != null && !pathToUse.startsWith("/")) { builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); builder.append('/'); } if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } builder.append(pathToUse); builder.append(']'); return builder.toString(); } 上述的name处理逻辑暂时先有个印象即可,后续会详细地跟踪源码进行讲解。 1.2. PropertySource注解加载指定的属性文件测试 上文我们设置了PropertySource注解来加载"classpath:config/jdbc-bainuo-dev.properties"文件。该文件的目录结构如下图所示: jdbc-bainuo-dev.properties文件内容如下: spring.datasource.shareniu.url=shareniu application.properties文件内容如下: spring.profiles.active=dev 上面的配置文件中,spring.profiles.active属性配置了当前使用的环境是dev。spring.datasource.shareniu.url只是一个普通的属性,本身并没有什么特殊的含义。 下面开始书写Springboot的启动类,如下所示: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(DemoApplication.class); ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); CustomerDataSourceConfig1 customerDataSourceConfig = configurableApplicationContext .getBean(CustomerDataSourceConfig1.class); System.out.print(customerDataSourceConfig); } } 运行上述的代码,程序的输出如下: CustomerDataSourceConfig{url='null'} 奇怪了,怎么url是空呢?PropertySource注解不是已经将jdbc-bainuo-dev.properties文件加载到当前的环境中了吗?我们不妨试一下看看jdbc-bainuo-dev.properties中的spring.datasource.shareniu.url属性是否可以获取到,进而从侧面验证PropertySource注解已经将jdbc-bainuo-dev.properties文件加载到当前的环境中。修改上述启动类的代码如下: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(DemoApplication.class); ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); CustomerDataSourceConfig1 customerDataSourceConfig = configurableApplicationContext.getBean(CustomerDataSourceConfig1.class); String property = configurableApplicationContext.getEnvironment().getProperty("spring.datasource.shareniu.url"); System.out.println(property); System.out.print(customerDataSourceConfig); } } 运行上述的代码,程序的输出如下: Shareniu 通过上述的代码可以看出PropertySource确实是生效了。那么我们怎么将spring.datasource.shareniu.url属性值自动注入到CustomerDataSourceConfig1 类中的url属性中呢? 1.3. PropertySource注解读取指定文件并将属性注入到配置类 Spring中提供了@Value注解,用于将配置文件中的属性值读取出来并设置到相应的属性中。在这里我们学习一下如何使用@Value注解。同样的还是以上文的两个类为例进行详细说明,首先需要修改CustomerDataSourceConfig1类,修改部分如下所示: @Component @PropertySource( name="jdbc-bainuo-dev.properties",value= {"classpath:config/jdbc-bainuo-dev.properties"},ignoreResourceNotFound=false,encoding="UTF-8") public class CustomerDataSourceConfig1 { @Value("${spring.datasource.shareniu.url}") private String url; } 上述的类中,在url字段中增加了@Value注解,并指定了SPEL表达式为${spring.datasource.shareniu.url}。再次运行springboot启动类,控制台的输出为shareniu。表明确实可以通过@Value进行属性值的注入。但是使用@Value注解方式有一个不太友好的地方就是,当项目中有大量的属性进行配置的时候,我们需要一个个的在类的字段中增加@Value注解,这样确实很费劲,不过我们可以通过Springboot提供的@ConfigurationProperties注解解决这个问题。 1.4. ConfigurationProperties注解使用 @ConfigurationProperties是类级别的注解,具体使用方式如下: @Component@ConfigurationProperties(prefix = "spring.datasource.shareniu") @PropertySource( name="jdbc-bainuo-dev.properties",value= {"classpath:config/jdbc-bainuo-dev.properties"},ignoreResourceNotFound=false,encoding="UTF-8")public class CustomerDataSourceConfig1 { private String url; } 上述代码中,在CustomerDataSourceConfig1类中增加了ConfigurationProperties注解,并且指明了属性的前缀为spring.datasource.shareniu。这样Springboot在处理的时候,会去扫描当前类中的所有字段并进行属性的查找以及组装。比如我们配置的prefix = "spring.datasource.shareniu",CustomerDataSourceConfig1类中有一个url字段,则url字段需要匹配的属性是prefix+字段=spring.datasource.shareniu.url。 那不仅有个疑问?如果指定的字段没有找到属性怎么办呢?这个可以进行如下的配置: @ConfigurationProperties(prefix = "spring.datasource.shareniu",ignoreUnknownFields=true,ignoreInvalidFields=true) ignoreUnknownFields:忽略未知的字段。 ignoreInvalidFields:是否忽略验证失败的字段。这个怎么理解呢?比如我们在配置文件中配置了一个字符串类型的变量,类中的字段是int类型,那肯定会报错的。如果出现这种情况我们可以容忍,则需要配置该属性值为true。该参数值默认为false。 本文暂且讲解到这里,后续的文章我们来讲解@PropertySource注解如何实现读取不同环境中的配置文件,这个不同环境的文件动态切换读取,PropertySource默认是不支持的,因此我们需要扩展该注解对应的源码。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:springboot源码分析10-ApplicationContextInitializer使用一文中,我们详细地讲解了ApplicationContextInitializer的三种使用方式,本文我们重点看一下为何这三种方式都可以使用,也就是框架是如何处理的。包括内置的ContextIdApplicationContextInitializer、DelegatingApplicationContextInitializer。 1.1. 用户手动添加ApplicationContextInitializer 首先,我们回想一下ApplicationContextInitializer实现方式一,示例代码如下: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(DemoApplication.class); springApplication.addInitializers(new ShareniuApplicationContextInitializer()); ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); } } 我们重点看一下springApplication.addInitializers方法如下所示: private List<ApplicationContextInitializer<?>> initializers; public void addInitializers(ApplicationContextInitializer<?>... initializers) { this.initializers.addAll(Arrays.asList(initializers)); } 上述中的ShareniuApplicationContextInitializer实力对象最终会存储在SpringApplication类中的initializers集合中。这个集合在哪里进行调用的呢?我们不禁有个疑问?文章稍后我们再过来看这个问题。 1.2. 系统内置的ApplicationContextInitializer 上文的代码中,实例化了SpringApplication类,该类的构造函数代码如下: public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ... setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); } 上述的代码逻辑中就涉及到了系统内置的一系列上下文初始化器的获取以及添加,我们看一下getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,相信只要认真看完前面的系列文章的朋友,就可以很快的知道这行代码的含义就是加载META-INF/spring.factories文件key为org.springframework.context.ApplicationContextInitializer的所有属性值,因此springboot源码分析10-ApplicationContextInitializer使用一文中的使用方式三就不难理解了。关于getSpringFactoriesInstances方法的相关执行逻辑可以参考springboot源码分析4-springboot之SpringFactoriesLoader使用。 spring-boot-2.0.0.M6.jar中META-INF/spring.factories文件key为org.springframework.context.ApplicationContextInitializer的所有属性值如下所示: org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer 上述的一系列逻辑执行完毕之后,所有的ApplicationContextInitializer最终将存储到SpringApplication类中的initializers集合中。 1.3. 触发ApplicationContextInitializer 接下来,我们继续讲问题回归到springApplication类中的run方法中,相关代码如下所示: public ConfigurableApplicationContext run(String... args) { ... prepareContext(context, environment, listeners, applicationArguments,printedBanner); ... } prepareContext方法的核心代码如下: private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) { ... applyInitializers(context); ... } prepareContext方法的各种初始化逻辑非常的复杂,因此这里我们才暂时先将关于ApplicationContextInitializer有关的代码罗列出来,防止一次性罗列之后,歪楼跑题。applyInitializers方法的实现逻辑如下: protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); initializer.initialize(context); } } 上述代码的处理逻辑如下: 1、从SpringApplication类中的initializers集合获取所有的ApplicationContextInitializer。也就是getInitializers()函数所做的事情。 2、循环调用ApplicationContextInitializer中的initialize方法。 讲解到这里之后,对于ApplicationContextInitializer中的使用方式1、3已经非常清楚了,那么通过在配置文件中配置context.initializer.classes进而设置ApplicationContextInitializer的方式貌似还没有看到踪迹(方式2)?上文中的initializers集合已经初始化了,然而方式2中的具体ApplicationContextInitializer并没有被添加到initializers集合中,这又是怎么回事呢?我们不妨看看一系列重要的内置ApplicationContextInitializer。 1.4. ApplicationContextInitializer集合排序 所有的ApplicationContextInitializer均可以实现order接口进行优先级的设置。 1. 基于Order值升序排序,反应的就是优先级的从高到底 2. 对于拥有相同Order值的对象,任意顺序 3. 对于不能排序的对象(没有实现Ordered接口,没有@Order注解或者@Priority注解),会排在最后,因为这类对象的优先级是最低的 具体的实现可以去看源代码,就不贴在这里了。AnnotationAwareOrderComparator扩展自OrderComparator,从而能够支持@Order注解以及javax.annotation.Priority注解。OrderComparator已经可以支持Ordered接口了。 1.5. DelegatingApplicationContextInitializer集合排序 DelegatingApplicationContextInitializer:顾名思义,这个初始化器实际上将初始化的工作委托给context.initializer.classes环境变量指定的初始化器(通过类名),也就是上文中提到的方式2内部实现机制。 该类的核心代码如下所示: private static final String PROPERTY_NAME = "context.initializer.classes"; public void initialize(ConfigurableApplicationContext context) { ConfigurableEnvironment environment = context.getEnvironment(); List<Class<?>> initializerClasses = getInitializerClasses(environment); if (!initializerClasses.isEmpty()) { applyInitializerClasses(context, initializerClasses); } } private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) { String classNames = env.getProperty(PROPERTY_NAME); List<Class<?>> classes = new ArrayList<>(); if (StringUtils.hasLength(classNames)) { for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) { classes.add(getInitializerClass(className)); } } return classes; } 上述的代码处理逻辑如下: 1、通过env获取到context.initializer.classes配置的值,如果有则直接获取到具体的值并进行实例化。 2、开始调用具体ApplicationContextInitializer类中的initialize方法。 这个初始化器的优先级是Spring Boot定义的4个初始化器中优先级别最高的,因此会被第一个执行。 1.6. ContextIdApplicationContextInitializer 这个类的作用是给ApplicationContext(上下文对象)设置一个id值。该类会尝试读取如下的属性: spring.application.name vcap.application.name spring.config.name vcap.application.instance_index spring.application.index server.port PORT spring.profiles.active 该类的核心代码如下: private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}"; private static final String INDEX_PATTERN = "${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}"; private String getApplicationId(ConfigurableEnvironment environment) { String name = environment.resolvePlaceholders(this.name); String index = environment.resolvePlaceholders(INDEX_PATTERN); String profiles = StringUtils.arrayToCommaDelimitedString(environment.getActiveProfiles()); if (StringUtils.hasText(profiles)) { name = name + ":" + profiles; } if (!"null".equals(index)) { name = name + ":" + index; } return name; } 上述的代码逻辑进行如下的总结: 1、首先获取名称。上述说的8个配置属性均可以使用spel表达式,因此这里进行了表达式的获取解析工作。也就是 environment.resolvePlaceholders所做的事情。名称的取值依赖如下几个属性。spring.application.name、vcap.application.name、spring.config.name、vcap.application.instance_index 2、获取索引。与name的获取道理一样,index的取值依赖如下几个属性。 vcap.application.instance_index、spring.application.index、server.port、PORT 3、获取spring.profiles.active的值,如果不为空,则name的值为name:spring.profiles.active的值. 4、如果index不为空,则最终name的值为name:spring.profiles.active的值:index的值。 下面我们通过一个例子进行详细地说明: 首先,我们在application.properties文件中配置如下几个属性: spring.application.name=shareniu spring.application.index=10001 spring.profiles.active=dev 书写一个测试类如下所示: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(DemoApplication.class); springApplication.addInitializers(new ShareniuApplicationContextInitializer()); ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); String id = configurableApplicationContext.getId(); System.out.println("id:==================="+id); } } 运行上述的类,控制台的输出信息如下: id:===================shareniu:dev:10001 关于给ApplicationContext(上下文对象)设置一个id值的高级用法,后续的实战章节中详尽的进行讲解。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:spring中ApplicationContextInitializer接口是在ConfigurableApplicationContext刷新之前初始化ConfigurableApplicationContext的回调接口。当spring框架内部执行 ConfigurableApplicationContext#refresh() 方法的时候回去回调。 1.1. 实现方式一 首先,我们需要自定义一个类并且实现ApplicationContextInitializer接口。示例代码如下: public class ShareniuApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { public void initialize(ConfigurableApplicationContext ac) { System.out.println("跟着分享牛学习springboot源码分析系列文章1"); } } 非常的简单,我们仅仅是输出了一行日志。在这里我们可以修改ac对象的各种属性值,毕竟他是个引用类型。 新建一个测试类进行测试,示例代码如下: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(DemoApplication.class); springApplication.addInitializers(new ShareniuApplicationContextInitializer()); ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); } } 运行上述代码,程序的输出如下: 1.2. 实现方式二 首先,我们需要自定义一个类并且实现ApplicationContextInitializer接口。示例代码如下: public class ShareniuApplicationContextInitializer1 implements ApplicationContextInitializer<ConfigurableApplicationContext> { public void initialize(ConfigurableApplicationContext ac) { System.out.println("跟着分享牛学习springboot源码分析系列文章2"); } } 然后在项目的根目录中新建一个application.properties。并进行如下的属性配置: context.initializer.classes=com.example.demo.ch10.ShareniuApplicationContextInitializer1 application.properties层级结构如下: 再次执行方式一的DemoApplication ,程序的输出如下图所示: Ok,这种方式也是可以实现的。 1.3. 实现方式三 首先,我们需要自定义一个类并且实现ApplicationContextInitializer接口。示例代码如下: public class ShareniuApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> { public void initialize(ConfigurableApplicationContext ac) { System.out.println("跟着分享牛学习springboot源码分析系列文章3"); } } 然后我们在项目的根目录中新建META-INF/spring.factories文件。目录结构如下所示: spring.factories文件的内容如下: org.springframework.context.ApplicationContextInitializer=\ com.example.demo.ch10.ShareniuApplicationContextInitializer2 再次执行方式一的DemoApplication ,程序的输出如下图所示: 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:springboot框架为我们提供了很多的便利,其中有一个非常有意思的功能,那就是可以通过变量的方式来配置一个随机数random,然后使用random随机出各式各样数值。本位重点讲解一下random的使用以及框架内部的实现机制。 1.1. Springboot中random的使用 首先我们定义一个配置类,如下所示: 1 @Component 2 public class Config { 3 @Value("${random.value}") 4 private String value; 5 @Value("${random.int}") 6 private int randomInt; 7 @Value("${random.long}") 8 private long randomLong; 9 @Value("${random.uuid}") 10 private String randomuuid; 11 @Value("${random.int(10)}") 12 private int randomInt1; 13 @Value("${random.int[1024,65536]}") 14 private int randomIntRange; 15 16 @Override 17 public String toString() { 18 return "Config [value=" + value + ", randomInt=" + randomInt + ", randomLong=" + randomLong + ", randomuuid=" 19 + randomuuid + ", randomInt1=" + randomInt1 + ", randomIntRange=" + randomIntRange + "]"; 20 } 21 } 上述类中我们配置的与random相关的表达式有: random.value返回值是string类型;random.int返回值是int类型;random.long返回值是long类型;random.uuid返回值是string类型;random.random.int(10)返回值是int类型;random.int[1024,65536]返回值是int类型。 紧接着,我们书写一个springboot启动类并打印Config类。示例代码如下: 1 @SpringBootApplication 2 public class DemoApplication { 3 public static void main(String[] args) { 4 BainuoSpringApplication springApplication = new BainuoSpringApplication(DemoApplication.class); 5 ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args); 6 Config config = configurableApplicationContext.getBean(Config.class); 7 System.out.println(config.toString()); 8 } 9 } 运行上述类,程序的输出如下: Config [value=d4143e016f2f89527af9290cdc5faf8d, randomInt=-1397177543, randomLong=-5400484272385792469, randomuuid=113b3970-ac05-4a31-8a6c-1145ee55b9f9, randomInt1=2, randomIntRange=36897] 1.2. Springboot中random内部实现机制 上述我们讲解了random的各种使用方式,心中不免有个疑惑。Springboot是如何处理这些random的呢?我们又该如何更好的使用呢?正所谓知其然,还要知其所以然。前面的系列文章中,我们详细讲解了环境属性的相关知识点,其中PropertySource类有一个子类RandomValuePropertySource,该类正是处理上文我们使用的random的。我们来快速学习下该类,示例代码如下: 1 public class RandomValuePropertySource extends PropertySource<Random> { 2 public static final String RANDOM_PROPERTY_SOURCE_NAME = "random"; 3 private static final String PREFIX = "random."; 4 private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class); 5 public RandomValuePropertySource(String name) { 6 super(name, new Random()); 7 } 8 public RandomValuePropertySource() { 9 this(RANDOM_PROPERTY_SOURCE_NAME); 10 } 11 @Override 12 13 private Object getRandomValue(String type) { 14 if (type.equals("int")) { 15 return getSource().nextInt(); 16 } 17 if (type.equals("long")) { 18 return getSource().nextLong(); 19 } 20 String range = getRange(type, "int"); 21 if (range != null) { 22 return getNextIntInRange(range); 23 } 24 range = getRange(type, "long"); 25 if (range != null) { 26 return getNextLongInRange(range); 27 } 28 if (type.equals("uuid")) { 29 return UUID.randomUUID().toString(); 30 } 31 return getRandomBytes(); 32 } 33 34 private String getRange(String type, String prefix) { 35 if (type.startsWith(prefix)) { 36 int startIndex = prefix.length() + 1; 37 if (type.length() > startIndex) { 38 return type.substring(startIndex, type.length() - 1); 39 } 40 } 41 return null; 42 } 43 44 private int getNextIntInRange(String range) { 45 String[] tokens = StringUtils.commaDelimitedListToStringArray(range); 46 int start = Integer.parseInt(tokens[0]); 47 if (tokens.length == 1) { 48 return getSource().nextInt(start); 49 } 50 return start + getSource().nextInt(Integer.parseInt(tokens[1]) - start); 51 } 52 53 private long getNextLongInRange(String range) { 54 String[] tokens = StringUtils.commaDelimitedListToStringArray(range); 55 if (tokens.length == 1) { 56 return Math.abs(getSource().nextLong() % Long.parseLong(tokens[0])); 57 } 58 long lowerBound = Long.parseLong(tokens[0]); 59 long upperBound = Long.parseLong(tokens[1]) - lowerBound; 60 return lowerBound + Math.abs(getSource().nextLong() % upperBound); 61 } 62 63 private Object getRandomBytes() { 64 byte[] bytes = new byte[32]; 65 getSource().nextBytes(bytes); 66 return DigestUtils.md5DigestAsHex(bytes); 67 } 68 69 public static void addToEnvironment(ConfigurableEnvironment environment) { 70 environment.getPropertySources().addAfter( 71 StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, 72 new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME)); 73 logger.trace("RandomValuePropertySource add to Environment"); 74 } 75 76 } 下面重点讲解一下getProperty方法,该方法的处理逻辑非常的简单,首先判断需要获取的变量是否是以random开头的,如果是则开始调用getRandomValue方法进行处理,并截取random.这个字符串。示例代码如下: 1 public Object getProperty(String name) { 2 if (!name.startsWith(PREFIX)) { 3 return null; 4 } 5 return getRandomValue(name.substring(PREFIX.length())); 6 } 比如上文的${random.value},在这里处理之后,传递给getRandomValue方法的参数值是value;同理${random.int}在这里处理之后,传递给getRandomValue方法的参数值是int。好了,继续看getRandomValue方法吧,该方法内部实现中,会调用类似getSource方法,该方法返回值正式Random实例对象。示例代码如下: 1 public RandomValuePropertySource(String name) { 2 super(name, new Random()); 3 } 了解了这些之后,getRandomValue方法的处理逻辑就非常简单了,总结梳理如下: 1. 如果是int则直接调用调用new Random().nextInt()。 2. 如果是long则直接调用调用new Random().nextLong()。 3. 如果是int或者long范围的则首先通过getRange获取到区间,然后还是调用new Random()中的区间随机数生成方法。 4. 如果是uuid则直接调用调用UUID.randomUUID().toString()。 5. 如果没有识别出来,则直接调用getRandomBytes生成一个字符串。 RandomValuePropertySource类的处理非常的简单而且经典,只要用一定的java基础均可以看明白。大家下去一步步debug即可看到起内部处理逻辑。但是这个类是怎么注入到当前springboot运行环境的呢?相信很多人有这样的疑问?这个涉及到了Springboot中的监听器使用,我们后续文章再来展开说明。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
上一文springboot源码分析7-环境属性构造过程(上)中详细讲解了springboot环境的各种初始化操作。本文我们继续探讨一下环境的一些知识点。 首先,我们看一下抽象基类AbstractEnvironment。该类中的构造函数如下: 1 private final MutablePropertySources propertySources = new MutablePropertySources(this.logger); 2 public AbstractEnvironment() { 3 customizePropertySources(this.propertySources); 4 } 该类的构造函数中会直接调用customizePropertySources方法,customizePropertySources方法是个空的方法,因此最终各种子类一定回去重写该方法的,这个我们上个章节详细地说明过。这里重点看下customizePropertySources方法中的入参propertySources,propertySources在AbstractEnvironment类中已经实例化了。而且是个对象因此是引用类型,因此所有的自类都是可以拿到这个对象的引用的。propertySources为MutablePropertySources类型。MutablePropertySources 我们需要重点的讲解一下。 1.1. MutablePropertySources MutablePropertySources类的层级结构图如下所示: MutablePropertySources类实现了PropertySources接口。该类中持有了所有的一个propertySourceList集合。propertySourceList集合中的元素类型是PropertySource。PropertySource类的作用,前面的章节也详细讲解了,因此这里不再赘述。MutablePropertySources中所定义的大部分想法均是对propertySourceList集合进行操作,比如在该集合的头部添加元元素、尾部添加元素、在指定的元素之后添加元素。该类的核心方法如下所示: 这里以addLast方法为例进行说明: 1 public void addLast(PropertySource<?> propertySource) { 2 removeIfPresent(propertySource); 3 this.propertySourceList.add(propertySource); 4 } 首先调用了removeIfPresent方法,然后直接将propertySource添加到propertySourceList集合中,removeIfPresent方法实现逻辑如下: 1 protected void removeIfPresent(PropertySource<?> propertySource) { 2 this.propertySourceList.remove(propertySource); 3 } 逻辑非常的简单,直接将propertySource从propertySourceList集合中进行移除。 在这里简单补充一点:所有的PropertySource以及子类均存储在propertySourceList集合中,而该集合是一个List类型,List是有顺序的,因此最终的元素添加顺序,决定了元素的最终遍历查找顺序,这里一定要注意。关于propertySourceList集合的元素添加方式,如下所示: 1 protected void customizePropertySources(MutablePropertySources propertySources) { 2 propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); 3 propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); 4 } 后续文章,我们重点讲解各种PropertySource以及子类的使用。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
使用springboot的目的就是在项目开发中,快速出东西,因此springboot对于配置文件的格式支持是非常丰富的,最常见的配置文件后缀有如下四种:properties、xml、yml、yaml,比如我们在springboot项目根目录中配置了一个application.properties文件,则springboot项目启动的时候就会自动将该文件的内容解析并设置到环境中,这样后续需要使用该文件中配置的属性的时候,只需要使用@value即可。同理application.xml、application.yml、application.yaml文件也会自动被加载并最终设置到环境中。 上面我们提到了环境,那么环境到底是个什么玩意呢?在这里提前说一下:我们这里关注的是源码层面的事情。并非讲解api如何使用。 大家首先思考一下,springboot项目如何启动,这个到很简单,无外乎引入springboot依赖包,设置项目启动的main方法如下所示: @EnableAutoConfiguration public class Application { private static Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { long startTime = System.currentTimeMillis(); SpringApplication.run(Application.class,args); logger.info("程序启动花费时间为:" + (System.currentTimeMillis() - startTime) / 1000 + "秒"); } } 上述的代码非常的简单,但是springboot做了非常多的事情,因为springboot代码体系非常庞大,所以后续的文章是我们讲解那些源码就直接看那些源码,把不需要了解的暂时放到一边。因此在这里暂时先关注环境的创建源码,我们快速定位到SpringApplication类中的public ConfigurableApplicationContext run(String... args)方法,该方法关于环境的准备代码如下所示: ... ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); ... prepareEnvironment方法从名字就可以看出来是准备环境(Environment),prepareEnvironment代码如下: private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { //获取或者创建环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置环境的信息 configureEnvironment(environment, applicationArguments.getSourceArgs()); //通知所有的观察者,环境已经准备好了。 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; } 接下来,我们一步步的分析。 1.1. 1.获取或者创建环境 getOrCreateEnvironment()方法如下所示: if (this.environment != null) { return this.environment; } if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment(); 上述代码逻辑如下: 1.如果environment不为空则直接返回。 2.如果是web环境则直接实例化StandardServletEnvironment类。 3.如果不是web环境则直接实例化StandardEnvironment类。 1.2. WebApplicationType类型 Springboot2版本开始增加了WebApplicationType的类型,其定义如下: public enum WebApplicationType { /** * 不需要再web容器的环境下运行,也就是普通的工程 */ NONE, /** 基于servlet的Web项目 */ SERVLET, /** 响应式web应用==reactive web Spring5版本的新特性 */ REACTIVE } 1.3. ConfigurableEnvironment类 environment 为ConfigurableEnvironment类型。我们不妨看一下该类的层次图如下所示: Environment接口是Spring对当前程序运行期间的环境的封装(spring)。主要提供了两大功能:profile和property(顶级接口PropertyResolver提供)。目前主要有StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment4种实现,分别代表普通程序、Web程序、测试程序的环境、响应式web环境。通过上述的getOrCreateEnvironment方法处理逻辑也是可以总结出来的。 StandardReactiveWebEnvironment是Springboot2新引入的,之前的版本没有这个类。关于这一个后续的章节会单独的详细讲解。 2.环境的装载 在上面的代码中实例化了StandardServletEnvironment类(我自己的环境是web),实例化该类的时候肯定会实例化其父类AbstractEnvironment,AbstractEnvironment类的构造函数如下: public AbstractEnvironment() { customizePropertySources(this.propertySources); } 需要注意一点,因为实例化的是StandardServletEnvironment类,jvm会自动触发其父类中的构造函数,但是当前程序的this指针依然是StandardServletEnvironment。 this.propertySources属性如下所示: AbstractEnvironment.java private final MutablePropertySources propertySources = new MutablePropertySources(this.logger); 我们继续跟踪customizePropertySources方法,如下所示: AbstractEnvironment.java protected void customizePropertySources(MutablePropertySources propertySources) { } 好吧,customizePropertySources方法竟然是个空的实现,但是注意一点,当前程序this是StandardServletEnvironment实例,我们不妨看一下StandardServletEnvironment类中是否重写了该方法。果不其然,StandardServletEnvironment类重写了customizePropertySources方法,详细代码如下所示: StandardServletEnvironment.java protected void customizePropertySources(MutablePropertySources propertySources) { //servletConfigInitParams propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); //servletContextInitParams propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); //jndiProperties if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } super.customizePropertySources(propertySources); } 上述的代码中,propertySources为AbstractEnvironment.java中的propertySources字段,因为他是个引用类型,所以可以拿到指针即可修改其值。 1.4. propertySources类 虽然我们暂时还不知道propertySources要干啥,但是我们还是先看明白PropertySources到底要干啥。PropertySources类可以参考springboot源码分析6-springboot之PropertySource类初探一文。 我们再次看一下customizePropertySources方法的实现: 首先添加servletConfigInitParams,然后添加servletContextInitParams,其次判断是否是jndi环境,如果是则添加jndiProperties,最后调用父类的customizePropertySources(propertySources)。 在跟进父类的customizePropertySources(propertySources)方法之前,我们总结一下MutablePropertySources类中propertySourceList已经存在的属性为servletConfigInitParams、servletContextInitParams、jndiProperties(如果存在)。 StandardEnvironment类为StandardServletEnvironment类的父类,该类的customizePropertySources方法如下: protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } 1、添加systemProperties 2、添加systemEnvironment。 上述的方法逻辑执行完毕之后,MutablePropertySources类中propertySourceList已经存在的属性为servletConfigInitParams、servletContextInitParams、jndiProperties(如果存在)、systemProperties、systemEnvironment。 经过一系列的跟踪getOrCreateEnvironment方法所做的事情已经分析完毕了。我们不妨继往下看。 3.配置环境信息 configureEnvironment(environment, applicationArguments.getSourceArgs())方法详细实现如下所示: protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); } 3.1配置属性源 configurePropertySources(environment, args)方法的核心实现如下: protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } } 1、如果defaultProperties不为空,则继续添加defaultProperties。思考一个问题defaultProperties怎么设置? 2、如果addCommandLineProperties为true并且有命令参数,分两步骤走:第一步存在commandLineArgs则继续设置属性;第二步commandLineArgs不存在则在头部添加commandLineArgs。 上述的代码执行完毕之后,MutablePropertySources类中propertySourceList已经存在的属性为commandLineArgs、servletConfigInitParams、servletContextInitParams、jndiProperties(如果存在)、systemProperties、systemEnvironment、defaultProperties(如果存在)。 3.2配置Profiles 这个后续我们用到了再来讲解。 本文我们暂时讲解到这里,后续的文章中,我们继续跟踪属性文件的加载规则以及加载过程。提前曝光一点: commandLineArgs、servletConfigInitParams、servletContextInitParams、jndiProperties(如果存在)、systemProperties、systemEnvironment、defaultProperties(如果存在)中的属性优先级从前到后依次降低。在最前面的使用优先级最高。 比如commandLineArgs中存在一个属性a=1; systemProperties中存在一个属性a=2,则我们程序使用的时候a=1,因为越靠前的优先级越高。通过上述的优先级我们可以发现一个规律,命令行的优先级最高、其次是程序中的、然后是系统的环境变量以及属性、最后是默认的。 propertySources接口我们下一节课重点进行分析。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:本小节重点梳理一下PropertySource类的相关结构以及职责,本文的学习前提是学习了springboot源码分析5-springboot之命令行参数以及原理一文。 在springboot源码分析5-springboot之命令行参数以及原理一文中,我们看到了实例化Source类的时候,会去先实例化其父类SimpleCommandLinePropertySource。SimpleCommandLinePropertySource类的构造函数中直接解析了命令行参数以及值,然后返回封装好的CommandLineArgs实例对象。SimpleCommandLinePropertySource类的构造函数中会继续调用其父类CommandLinePropertySource,CommandLinePropertySource继续调用其父类EnumerablePropertySource,EnumerablePropertySource继续调用其父类PropertySource。PropertySource类是一个抽象基类。 1.1. PropertySource类 PropertySource类是一个抽象类,代码如下: 1 public abstract class PropertySource<T> { 2 protected final String name; 3 protected final T source; 4 } getSource()方法:这个方法会返回得到属性源的源头。比如MapPropertySource的源头就是一个Map,PropertiesPropertySource的源头就是一个Properties。 name:我们可以理解为一个map中的key。 PropertySource类的继承结构图如下图所示: l RandomValuePropertySource:source是random。 l ServletConfigPropertySource:source是ServletConfig。 l ServletContextPropertySource:source是ServletContext。 l JndiPropertySource:source是JndiLocatorDelegate。 l StubPropertySource:source是Object。 l MapPropertySource:source是Map<String, Object>。 本文内容暂时讲解道这里,后面的文章中讲解环境变量的时候,再来一个个的看这些PropertySource子类。 未知参数最终被添加到了nonOptionArgs集合。 讲解到这里基本上命令行参数的设置以及解析原理都搞明白了。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:本文我们重点分析一下Springboot框架中的命令行参数的使用以及框架内部处理的命令行参数的原理。 众所周知,springboot项目可以有两种方式启动,第一种使用jar包;第二种使用war包。在使用jar方式的时候,我们可以在启动jar包的时候设置一些命令参数。 1.1 命令行参数使用 首先我们看一下如何使用在项目启动的时候设置命令行参数以及值。我这里使用的开发工具是Spring Tool Suite 版本是: 3.9.0.RELEASE。我们先建立一个工程文件,目录结构如下图所示: Application内容如下: 1 @EnableConfigurationProperties 2 public class Application { 3 public static void main(String[] args) { 4 ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Application.class, args); 5 } 6 } 怎么启动上述的类呢?操作步骤如下: 第一步:点击 Debug AS-->>Debug Configurations 第二步: 首先,我们需要点击①箭头处的新增按钮,然后就会建立一个Spring Boot App、其次我们输入命令行的参数,然后点击App即可完成设置并启动项目。上图中我们设置了三个变量,如下所示: --foo=bar -foo=bar1 --foo=bar2 上述这三个变量我们该如何获取呢?这也是校验参数是否设置成功的一个途径吧相信大家都想知道下一步的操作,下面的实例代码还是在上文的两个步骤为前提下进行。实例代码如下: 1 @EnableConfigurationProperties 2 public class Application { 3 public static void main(String[] args) { 4 ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Application.class, args); 5 String property = configurableApplicationContext.getEnvironment().getProperty("foo"); 6 System.out.println(property); 7 } 8 } 运行上述的代码,控制台的输出信息如下: bar,bar2 我们通过ConfigurableApplicationContext实例获取环境ConfigurableEnvironment实例对象,然后通过ConfigurableEnvironment获取属性foo。ConfigurableEnvironment大家暂时先有个印象,我们通过这个实例对象获取到项目中所有的配置属性信息。这个我们后续也会详细的进行讲解。看到上面的输出,发现输出的是bar,bar2 ,然而bar1并没有输出?为什么呢?大家看下这三个命令行参数有何迥异,很显然bar,bar2的参数属性都是--开头的,而bar1是-开头的。那我们就很好奇springboot是如何获取这些参数值以及解析的呢? 上述中的启动类是Application,该类中的main方法去启动Springboot项目,既然是main方法,所以我们上文提到的三个命令行参数最终会被设置到args参数中的。这一点大家一定要注意了。SpringApplication.run方法会将args参数继续传递到SpringApplication类中让框架处理的。接下来,我们看一下SpringApplication.run方法中关于命令行参数的相关处理逻辑吧。 1.2 命令行参数原理 我们开始跟进SpringApplication类中的run(String... args)方法,相关的代码如下所示: 1 public ConfigurableApplicationContext run(String... args) { 2 ...//省略 3 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); 4 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); 5 ...//省略 6 } 1.2.1 DefaultApplicationArguments类 首先,我们看一下DefaultApplicationArguments类,该类的核心代码如下: 1 public class DefaultApplicationArguments implements ApplicationArguments { 2 private final Source source; 3 private final String[] args; 4 public DefaultApplicationArguments(String[] args) { 5 Assert.notNull(args, "Args must not be null"); 6 this.source = new Source(args); 7 this.args = args; 8 } DefaultApplicationArguments类的构造函数中,首先实例化Source类,并将args参数以及值进行传递。然后在自身类中使用args属性进行参数值的报错,DefaultApplicationArguments类实现了ApplicationArguments接口。ApplicationArguments 接口中定义了各种命令行参数的操作,比如参数值的获取、参数名称的获取等方法。 上面的代码感觉没有什么神奇的地方,貌似只是对命令行参数的各种封装而已。其实Source类大有玄机。该类的代码如下所示: 1 private static class Source extends SimpleCommandLinePropertySource { 2 Source(String[] args) { 3 super(args); 4 } 5 ...//省略 6 } Source类继承SimpleCommandLinePropertySource类,并在当前类的构造函数调用SimpleCommandLinePropertySource 类的构造函数,SimpleCommandLinePropertySource 类的构造函数如下所示: 1 public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> { 2 public SimpleCommandLinePropertySource(String... args) { 3 super(new SimpleCommandLineArgsParser().parse(args)); 4 } 虽然SimpleCommandLinePropertySource类的构造函数继续调用CommandLinePropertySource类的构造函数进行处理,但是我们只需要将new SimpleCommandLineArgsParser().parse(args)这行代码搞明白,关于命令行参数以及值的处理我们就可以搞明白了。接下来我们快速看一下parse方法的实现逻辑,实例代码如下: 1 public CommandLineArgs parse(String... args) { 2 CommandLineArgs commandLineArgs = new CommandLineArgs(); 3 for (String arg : args) { 4 if (arg.startsWith("--")) { 5 String optionText = arg.substring(2, arg.length()); 6 String optionName; 7 String optionValue = null; 8 if (optionText.contains("=")) { 9 optionName = optionText.substring(0, optionText.indexOf("=")); 10 optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length()); 11 } 12 else { 13 optionName = optionText; 14 } 15 if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) { 16 throw new IllegalArgumentException("Invalid argument syntax: " + arg); 17 } 18 commandLineArgs.addOptionArg(optionName, optionValue); 19 } 20 else { 21 commandLineArgs.addNonOptionArg(arg); 22 } 23 } 24 return commandLineArgs; 25 } 我们将上述代码的逻辑梳理如下: ① 实例化CommandLineArgs类。这个类封装了命令行解析之后的参数以及值信息、还有没有被识别的命令行参数以及值的信息。 ② 循环遍历所有的参数以及值args,比如我们传递的是--foo=bar -foo=bar1 --foo=bar2 ③ 如果参数以--开头,则开始如下的操作;否则将其作为不能识别的参数进行处理,上文我们定义的-foo就是不能被识别的参数。 截取--字符串并使用optionText变量进行存储,比如参数--foo=bar截取之后,则optionText值为foo=bar。 optionName为foo,optionValue为bar,通过上述代码逻辑可以看出,如果=前边或者后边出现了空格就惨了。因为这个地方的代码并没有对空格以及特殊字符进行区分。 1.2.1.1. 识别参数添加 commandLineArgs.addOptionArg(optionName, optionValue);进行springboot可识别的命令行参数的添加工作,其内部实现逻辑如下所示: 1 private final Map<String, List<String>> optionArgs = new HashMap<>(); 2 public void addOptionArg(String optionName, @Nullable String optionValue) { 3 if (!this.optionArgs.containsKey(optionName)) { 4 this.optionArgs.put(optionName, new ArrayList<>()); 5 } 6 if (optionValue != null) { 7 this.optionArgs.get(optionName).add(optionValue); 8 } 9 } 上述代码中,首先校验optionName参数值是否存在于optionArgs集合中。如果不存在,则直接实例化ArrayList并将其添加到optionArgs集合中。 注意:optionArgs的key为参数的名称,value是一个List集合,存储的是该参数的所有值。通过这里的处理我们可以看出,我们上文输出的foo参数的值是bar,bar2就很容易理解了。 1.2.1.2. 未知参数添加 commandLineArgs.addNonOptionArg(arg)方法进行未知参数的添加逻辑,比如上文中的-foo=bar1参数就在这个方法进行处理的额,因为该参数不是--开头的。commandLineArgs.addNonOptionArg(arg)方法如下所示: 1 private final List<String> nonOptionArgs = new ArrayList<>(); 2 public void addNonOptionArg(String value) { 3 this.nonOptionArgs.add(value); 4 } 未知参数最终被添加到了nonOptionArgs集合。 讲解到这里基本上命令行参数的设置以及解析原理都搞明白了。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。
摘要:本文我们重点分析一下Spring框架中的SpringFactoriesLoader类以及META-INF/spring.factories的使用。在详细分析之前,我们可以思考一个问题?在我们设计一套API供别人调用的时候,如果同一个功能的要求特别多,或者同一个接口要面对很复杂的业务场景,这个时候我们该怎么办呢?其一:我们可以规范不同的系统调用,也就是传递一个系统标识;其二:我们在内部编码的时候可以使用不同的条件判断语句进行处理;其三:我们可以写几个策略类来来应对这个复杂的业务逻辑,比如同一个功能的实现,A实现类与B实现类的逻辑一点也不一样,但是目标是一样的,这个时候使用策略类是毋庸置疑的?上述的问题我们是在API层面进行处理的?那万一有一天让我们自己设计一套框架,然后让别人直接使用?我们该如何处理上述的这个问题呢?这个可能就涉及到SPI的一些规范了跟技巧了,比如同一套API可能有很多实现类,这个时候我们该如何内置一系列实现类供框架使用呢?或者让用户也可以自定义这些API的实现类,相互之间协作运转。带着这些问题我们看一下Spring框架中的SpringFactoriesLoader以及META-INF/spring.factories的使用。 1.1 属性配置 首先,我们来看一下属性的配置方式,在传统的开发模式中(无springboot),属性文件的格式无外乎就是两种,第一种是XML,第二种是key、value形式(properties文件)。当然springboot引入了yaml方式。这里我们重点看一下XML以及properties的定义以及获取方式。 1.1.1 properties方式 1.1.1.1. 单个属性配置 首先,我们新建一个shareniu-single.factories文件,该文件的目录结构如下图所示: shareniu-single.factories的内容如下: shareniu=http://www.shareniu.com/ 1.1.1.2. 多个属性配置 单个属性的定义比较简单,就是key、value形式即可。对于同一个属性有多个值的定义格式如下: com.example.demo.ch3.IShareniu=\ com.example.demo.ch3.ShareniuA,\ com.example.demo.ch3.ShareniuB 1.1.1.3. properties读取工具类 上述的属性定义完毕之后,我们写一个工具类进行测试,在这里我们直接调用了PropertiesLoaderUtils类中的方法。实例代码如下: 1 public class PropertiesLoaderUtilsTest { 2 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/shareniu-single.factories"; 3 public static void main(String[] args) throws IOException { 4 Properties properties = PropertiesLoaderUtils.loadAllProperties(FACTORIES_RESOURCE_LOCATION,null); 5 System.out.println(properties); 6 } 7 } 上述的代码直接调用了PropertiesLoaderUtils类中的loadAllProperties方法,PropertiesLoaderUtils的全路径名称为:org.springframework.core.io.support.PropertiesLoaderUtils。该类位于spring-core-5.0.0.RC3.jar包中。 运行上面的代码,程序的输出如下: {shareniu=http://www.shareniu.com/, com.example.demo.ch3.IShareniu=com.example.demo.ch3.ShareniuA,com.example.demo.ch3.ShareniuB} 果真我们自定义的属性都可以完美的获取到。 关于PropertiesLoaderUtils.loadAllProperties的核心代码如下: 1 public static Properties loadAllProperties(String resourceName, @Nullable ClassLoader classLoader) throws IOException { 2 ClassLoader classLoaderToUse = classLoader; 3 if (classLoaderToUse == null) { 4 classLoaderToUse = ClassUtils.getDefaultClassLoader(); 5 } 6 Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) : 7 ClassLoader.getSystemResources(resourceName)); 8 Properties props = new Properties(); 9 while (urls.hasMoreElements()) { 10 URL url = urls.nextElement(); 11 URLConnection con = url.openConnection(); 12 ResourceUtils.useCachesIfNecessary(con); 13 InputStream is = con.getInputStream(); 14 try { 15 if (resourceName.endsWith(XML_FILE_EXTENSION)) { 16 props.loadFromXML(is); 17 } 18 else { 19 props.load(is); 20 } 21 } 22 finally { 23 is.close(); 24 } 25 } 26 return props; 27 } loadAllProperties方法,首先会根据类加载器去获取指定的资源(也就是我们调用的时候,传递的resourceName参数值)。然后判断资源的后缀是否为xml,如果后缀是xml则使用xml方式加载资源,否则都是用Properties方式进行资源的加载。 注意:虽然上述的代码我们指定的资源名称是:META-INF/shareniu-single.factories,但是上述的类加载器不仅扫描我们项目的META-INF/shareniu-single.factories,还会扫描当前类加载所加载的jar包中的META-INF/shareniu-single.factories文件。 1.1.2 xml方式 了解了上述代码的处理逻辑之后,我们看一下xml方式如何定义,shareniu.xml定义内容如下: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 3 <properties> 4 <comment>shareniu xml配置文件</comment> 5 <entry key="username">shareniu</entry> 6 <entry key="url">http://www.shareniu.com/</entry> 7 </properties> 运行上述的属性工具类,控制台的输出信息如下: {url=http://www.shareniu.com/, username=shareniu} 1.2 SpringFactoriesLoader使用 了解了XML以及properties的定义以及获取方式之后,接下来学习SpringFactoriesLoader类就简单的多了。 首先,看一下SpringFactoriesLoader类定义的方法如下所示: 1. loadFactoryNames:加载指定的factoryClass并进行实例化。 2. loadSpringFactories:加载指定的factoryClass。 3. instantiateFactory:对指定的factoryClass进行实例化。 通过上文可知:loadFactoryNames方法内部直接调用loadSpringFactories方法,loadSpringFactories方法则会调用instantiateFactory方法。 loadSpringFactories方法内部会加载META-INF/spring.factories文件,这里加载的文件不仅包含项目中的,还包换我们项目环境所依赖的jar包中的META-INF/spring.factories文件。 1.现在,我们写一个简单的测试类,加载spring.factories文件,实例代码如下: spring.factories文件的内容如下所示: com.example.demo.ch3.IShareniu=\ com.example.demo.ch3.ShareniuA,\ com.example.demo.ch3.ShareniuB 其中:IShareniu为接口,ShareniuA以及ShareniuB实现了IShareniu接口。结构如下图所示: 2.自定义测试类并调用SpringFactoriesLoader类中的相关方法,如下所示: 1 public class DemoApplication { 2 public static void main(String[] args) { 3 List<String> loadFactoryNames = SpringFactoriesLoader.loadFactoryNames(IShareniu.class, null); 4 System.out.println(loadFactoryNames); 5 } 6 } 自行上述代码,程序的输出信息如下: [com.example.demo.ch3.ShareniuA, com.example.demo.ch3.ShareniuB] 通过上述的代码可知,我们确实完成了自身项目中META-INF/spring.factories文件的属性读取。 那我们能否能够通过Spring框架实例化这些类呢?答案是肯定的?实例代码如下: 1 List<IShareniu> loadFactories = SpringFactoriesLoader.loadFactories(IShareniu.class, null); 2 System.out.println(loadFactories); 自行上述代码,程序的输出信息如下: [com.example.demo.ch3.ShareniuA@53fd30, com.example.demo.ch3.ShareniuB@cbc42f] loadFactories方法返回的已经是实例化完毕的对象了。 1.3 SpringFactoriesLoader原理 接下来,我们看一下SpringFactoriesLoader类中的loadFactories方法,如下所示: 1 public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) { 2 Assert.notNull(factoryClass, "'factoryClass' must not be null"); 3 ClassLoader classLoaderToUse = classLoader; 4 if (classLoaderToUse == null) { 5 classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); 6 } 7 List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); 8 if (logger.isTraceEnabled()) { 9 logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); 10 } 11 List<T> result = new ArrayList<>(factoryNames.size()); 12 for (String factoryName : factoryNames) { 13 result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); 14 } 15 AnnotationAwareOrderComparator.sort(result); 16 return result; 17 } loadFactories方法首先获取类加载器,然后调用loadFactoryNames方法获取所有的制定资源的名称集合、其次调用instantiateFactory方法实例化这些资源类并将其添加到result集合中。最后调用AnnotationAwareOrderComparator.sort方法进行集合的排序。 1.3.1 loadFactoryNames方法 loadFactoryNames方法核心代码如下: 18 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 19 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 20 MultiValueMap<String, String> result = cache.get(classLoader); 21 if (result != null) 22 return result; 23 try { 24 Enumeration<URL> urls = (classLoader != null ? 25 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : 26 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 27 result = new LinkedMultiValueMap<>(); 28 while (urls.hasMoreElements()) { 29 URL url = urls.nextElement(); 30 UrlResource resource = new UrlResource(url); 31 Properties properties = PropertiesLoaderUtils.loadProperties(resource); 32 for (Map.Entry<?, ?> entry : properties.entrySet()) { 33 List<String> factoryClassNames = Arrays.asList( 34 StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); 35 result.addAll((String) entry.getKey(), factoryClassNames); 36 } 37 } 38 cache.put(classLoader, result); 39 return result; 40 } 41 catch (IOException ex) { 42 } 43 } loadSpringFactories方法直接加载所有的META-INF/spring.factories文件内容,其内部还是调用PropertiesLoaderUtils.loadProperties方法进行处理。该方法前面我们也详细的演示了,再次不再累赘。 唯一需要了解的是,这个地方使用了缓存策略。 1.3.2 instantiateFactory方法 instantiateFactory方法的核心逻辑如下: 1 private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) { 2 try { 3 Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader); 4 if (!factoryClass.isAssignableFrom(instanceClass)) { 5 throw new IllegalArgumentException( 6 } 7 return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance(); 8 } 9 catch (Throwable ex) { 10 throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex); 11 } 12 } 直接调用了ClassUtils.forName方法,然后调用ReflectionUtils.accessibleConstructor方法进行实例对象进行对象的实例化工作,原来这里直接使用了反射技术进行对象的实例化工作。原来如此。 至此,XML以及properties的定义以及获取方式,SpringFactoriesLoader类的使用以及原理已经讲解完毕。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。 作者:分享牛 出处:http://blog.csdn.net/qq_30739519 本博客中未标明转载的文章归作者分享牛所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
继续上文的<<springboot源码分析2-springboot 之banner定制以及原理章节>>进行讲解,上一节我们详细详解了banner的三种输出模式、banner的输出模式设置、banner类的架构、SpringApplicationBannerPrinter类、ImageBanner以及TextBanner的处理方式。本小节我们来重点讲解一下各种banner处理类的相关实现逻辑以及设计意图和职责。 1.1 SpringBootBanner类 SpringBootBanner类为默认的banner处理类。该类的核心代码如下所示: 1 class SpringBootBanner implements Banner { 2 private static final String[] BANNER = { "", 3 " . ____ _ __ _ _", 4 " /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", 5 "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\", 6 " \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", 7 " ' |____| .__|_| |_|_| |_\\__, | / / / /", 8 " =========|_|==============|___/=/_/_/_/" }; 9 private static final String SPRING_BOOT = " :: Spring Boot :: "; 10 private static final int STRAP_LINE_SIZE = 42; 11 @Override 12 public void printBanner(Environment environment, Class<?> sourceClass,PrintStream printStream) { 13 for (String line : BANNER) { printStream.println(line); 14 } 15 String version = SpringBootVersion.getVersion(); 16 version = (version == null ? "" : " (v" + version + ")"); 17 String padding = ""; 18 while (padding.length() < STRAP_LINE_SIZE 19 - (version.length() + SPRING_BOOT.length())) { 20 padding += " "; 21 } 22 printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, 23 AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version)); 24 printStream.println(); 25 } 26 27 } 看到上述的BANNER变量,是不是很眼熟,没错这个就是springboot启动的时候,默认的输出图案。我们再次看一下springboot启动的时候默认输出的图案,如下图所示: BANNER变量的内容正是上图中的上部分。也就是SPRING字符串。 注意:BANNER变量是一个字符串数组,数组的每一个元素内容均对应上图中的每一行字符串。为何这样设计呢?我们思考一下?这样设计的目的究其原因是为了防止定义一个大的字符串而出现跑位或者字符串便宜的问题。 接下来的重心看一下printBanner方法的执行逻辑: 1、循环遍历BANNER数组,并依次进行数组内容的打印。代码为printStream.println(line)。 2、调用SpringBootVersion.getVersion(),进行springboot版本信息的获取工作,这种方式我们在前面的系列文章中也详细讲解了,再次不再累赘。 3、对version字符串进行再加工,因此version最终的值为 (v2.0.0.M3)。 4、开始拼接:: Spring Boot ::和(v2.0.0.M3)的值,最终的效果如上图所示。通过while循环的逻辑可知、一行的字符串长度是42个。 5、开始打印字符串,这个大家有兴趣可以自己跟踪一下,相对而言比较简单。主要看下AnsiOutput.toString方法即可。 至此、springboot默认的banner图案以及输出逻辑我们已经梳理完毕。 1.2 PrintedBanner类 PrintedBanner类中的核心代码如下: 1 private static class PrintedBanner implements Banner { 2 private final Banner banner; 3 private final Class<?> sourceClass; 4 PrintedBanner(Banner banner, Class<?> sourceClass) { 5 this.banner = banner; 6 this.sourceClass = sourceClass; 7 } 8 public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) { 9 sourceClass = (sourceClass == null ? this.sourceClass : sourceClass); 10 this.banner.printBanner(environment, sourceClass, out); 11 } 12 } PrintedBanner类看上去貌似没有什么神奇的作用,但是这个类却有点装饰者模式的味道,该类的构造函数需要一个Banner 类型以及Class类型的参数。在printBanner方法中,唯一做的事情就是判断并设置sourceClass参数,然后直接调用banner的printBanner方法。为何这样设计呢?其实这个地方病没有太多神秘的地方。我们只需要知道一点sourceClass是可以自己定义的即可。sourceClass类又是做什么的呢?关于这点我们稍有印象即可,稍后即可看到。 1.3 ImageBanner ImageBanner类的核心实现如下所示: 1 public void printBanner(Environment environment, Class<?> sourceClass,PrintStream out) { 2 String headless = System.getProperty("java.awt.headless"); 3 try { 4 System.setProperty("java.awt.headless", "true"); 5 printBanner(environment, out); 6 } 7 catch (Throwable ex) { 8 ...... 9 } 10 finally { 11 if (headless == null) { 12 System.clearProperty("java.awt.headless"); 13 } 14 else { 15 System.setProperty("java.awt.headless", headless); 16 } 17 } 18 } 上述的代码主要从如下几个步骤进行。 1、获取系统环境变量中的java.awt.headless变量。 2、设置java.awt.headless变量值为true。并调用printBanner方法进行图案的打印工作。 3、finally中还原操作系统中的java.awt.headless环境变量值。 细心的朋友就会发现上述的步骤2有问题的。如果系统中已经设置java.awt.headless变量值为true,还有必要再设置一次吗?很显然,这个地方的代码可以改进下,加一个if判断。 1.3.1 java.awt.headless 模式 下面补充下java.awt.headless的相关知识点。 1. 什么是 java.awt.headless?Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。2. 何时使用和headless mode?Headless模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其是服务器端程序开发者。因为服务器(如提供Web服务的主机)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的显示设备、键盘和鼠标的主机)。 1.3.2 printBanner方法 继续看一下printBanner方法吧,该方法的实例代码如下: 1 private void printBanner(Environment environment, PrintStream out)throws IOException { 2 int width = environment.getProperty("banner.image.width", Integer.class, 76); 3 int height = environment.getProperty("banner.image.height", Integer.class, 0); 4 int margin = environment.getProperty("banner.image.margin", Integer.class, 2); 5 boolean invert = environment.getProperty("banner.image.invert", Boolean.class,false); 6 BufferedImage image = readImage(width, height); 7 printBanner(image, margin, invert, out); 8 } 上述的方法开始获取banner.image.width、banner.image.height、banner.image.margin、banner.image.invert四个属性值。这几个属性均可以在application.properties中,或者通过命令行参数进行相应的设置。如下图所示: 上述四个参数的含义、默认值说明: banner.image.width":默认值76,图案的宽度 "banner.image.height":默认值0,图案的高度 "banner.image.margin":默认值 2,空字符的数量 "banner.image.invert":默认值false,是否颠倒 在这里,我们只需要记住上述的几个变量是可以配置的即可,关于图片流的读取以及输出,在这里我们就不详细讲解了。 1.4 ResourceBanner ResourceBanner类为资源Banner。这个类可能很少有人使用到,但是麻雀虽小五脏俱全,这个类涉及到的知识点不少,我们先看下如何使用这个类。实例代码如下: 1 @SpringBootApplication 2 public class DemoApplication { 3 public static void main(String[] args) { 4 SpringApplication springApplication=new SpringApplication(); 5 springApplication.setBannerMode(Banner.Mode.CONSOLE); 6 Resource resource=new ClassPathResource("banner.txt"); 7 springApplication.setBanner(new ResourceBanner(resource)); 8 springApplication.run(DemoApplication.class, args); 9 } 10 } 我们实例化了一个ClassPathResource类并传了字符串banner.txt。在这里还要脑补一下Spring中的东西,那就是Resource 。 Resource是抽象了所有的配置文件以及属性文件、在Spring框架看来所有的文件、网络资源、jar、属性配置文件等都是资源,因此也提供了不同的资源读取类,其中ClassPathResource就是读取ClassPath路径中的一些资源文件,这里我们传递的是banner.txt,该文件的内容信息如下:分享牛原创网:${application.title} 其中application.title定义在application.properties中,如下所示: application.title=http://www.shareniu.com/ 运行上述代码程序的输出如下: 分享牛原创:http://www.shareniu.com/ 这个类确实有点意思,不仅支持自定义资源文件的读取,而且还支持Spring中的spel表达式。我们迫不及待的要去看看ResourceBanner类。 ResourceBanner类的printBanner方法如下所示: 1 public void printBanner(Environment environment, Class<?> sourceClass,PrintStream out) { 2 try { 3 String banner = StreamUtils.copyToString(this.resource.getInputStream(), 4 environment.getProperty("banner.charset", Charset.class,Charset.forName("UTF-8"))); 5 for (PropertyResolver resolver : getPropertyResolvers(environment,sourceClass)) { 6 banner = resolver.resolvePlaceholders(banner); 7 } 8 out.println(banner); 9 } 10 catch (Exception ex) { 11 } 12 } 上述方法的执行逻辑进行如下的总结: 1、获取resource中的输入流,并将其转化为字符串。 2、通过environment获取banner.charset变量,如果不存在,则默认使用UTF-8编码。在这里我们再次啰嗦一句话,springboot中所有的配置属性信息最后都会封装为environment中去,因此可以通过environment获取到项目中所有的配置属性信息。 3、循环遍历所有的PropertyResolver 去解析banner中配置的spel表达式。比如上文中的${application.title}就是在这个步骤进行处理的。 4、打印字符串信息。 1.4.1 PropertyResolvers集合初始化 上述中的第二步调用了getPropertyResolvers方法,该方法的实例代码如下: 1 protected List<PropertyResolver> getPropertyResolvers(Environment environment, 2 Class<?> sourceClass) { 3 List<PropertyResolver> resolvers = new ArrayList<>(); 4 resolvers.add(environment); 5 resolvers.add(getVersionResolver(sourceClass)); 6 resolvers.add(getAnsiResolver()); 7 resolvers.add(getTitleResolver(sourceClass)); 8 return resolvers; 9 } getPropertyResolvers方法的执行逻辑如下: 1、实例化resolvers集合,并添加environment元素,Environment接口继承自PropertyResolver接口。 2、调用getVersionResolver(sourceClass)方法并将其返回值添加到resolvers集合。getVersionResolver(sourceClass)方法的实现如下所示: 1 private PropertyResolver getVersionResolver(Class<?> sourceClass) { 2 MutablePropertySources propertySources = new MutablePropertySources(); 3 propertySources.addLast(new MapPropertySource("version", getVersionsMap(sourceClass))); 4 return new PropertySourcesPropertyResolver(propertySources); 5 } 6 private Map<String, Object> getVersionsMap(Class<?> sourceClass) { 7 String appVersion = getApplicationVersion(sourceClass);//获取sourceClass所在包的版本号 8 String bootVersion = getBootVersion();//获取Boot版本号,我使用的版本是v2.0.0.M3 9 Map<String, Object> versions = new HashMap<>(); 10 versions.put("application.version", getVersionString(appVersion, false)); 11 versions.put("spring-boot.version", getVersionString(bootVersion, false)); 12 versions.put("application.formatted-version", getVersionString(appVersion, true)); 13 versions.put("spring-boot.formatted-version",getVersionString(bootVersion, true)); 14 return versions; 15 } 上述代码中,直接实例化MutablePropertySources类,并将其添加到环境propertySources中,在这里在强调一点,环境变量的相关知识点后续会专门单独一章进行讲解。大家可以理解为propertySources是所有的属性容器就够了,我们可以通过propertySources获取到项目中配置的所有属性以及值。 上述的代码设置了如下几个属性以及值:application.version、spring-boot.version、application.formatted-version、spring-boot.formatted-version。其中application.version以及application.formatted-version两个我们可以自定义设置。 上述的几个属性,我将其打印了,输出信息如下: {application.formatted-version=, application.version=, spring-boot.formatted-version= (v2.0.0.M3), spring-boot.version=2.0.0.M3} 3、调用getAnsiResolver(sourceClass)方法并将其返回值添加到resolvers集合。getAnsiResolver(sourceClass)方法的实现如下所示: 1 private PropertyResolver getAnsiResolver() { 2 MutablePropertySources sources = new MutablePropertySources(); 3 sources.addFirst(new AnsiPropertySource("ansi", true)); 4 return new PropertySourcesPropertyResolver(sources); 5 } 直接设置开启了ansi。讲解到环境变量的时候一起进行讲解。 4、调用getTitleResolver(sourceClass)方法并将其返回值添加到resolvers集合。getTitleResolver(sourceClass)方法的实现如下所示: 1 private PropertyResolver getTitleResolver(Class<?> sourceClass) { 2 MutablePropertySources sources = new MutablePropertySources(); 3 String applicationTitle = getApplicationTitle(sourceClass); 4 Map<String, Object> titleMap = Collections.<String, Object>singletonMap( 5 "application.title", (applicationTitle == null ? "" : applicationTitle)); 6 sources.addFirst(new MapPropertySource("title", titleMap)); 7 return new PropertySourcesPropertyResolver(sources); 8 } 获取当前启动类中所在的包中的Implementation-Title属性值,并将其添加到sources中。 关于占位符的替换,我们后续的文章开始展开讲解。饭要一口口的吃嘛。至此各种Banner类已经讲解完毕。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。 作者:分享牛 出处:http://blog.csdn.net/qq_30739519 本博客中未标明转载的文章归作者分享牛所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
1. springboot源码分析2-springboot 之banner定制以及原理 springboot在启动的时候,默认会在控制台输出默认的banner。也就是我们经常所说的图案,输出的图案如下所示: . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.0.M3) 本小节围绕上述的图案也就是banner展开进行说明:包括banner的输出模式、定制、关闭、原理分析。 1.1 banner的三种输出模式 banner的输出默认有三种种模式,LOG、CONSOLE、OFF。 1. LOG:将banner信息输出到日志文件。 2. CONSOLE:将banner信息输出到控制台。 3. OFF:禁用banner的信息输出。 1.2 banner的输出模式设置 上述的三种输出模式,我们可以在程序中进行设置,示例代码如下: 1 @SpringBootApplication 2 public class DemoApplication { 3 public static void main(String[] args) { 4 SpringApplication springApplication=new SpringApplication(); 5 springApplication.setBannerMode(Banner.Mode.CONSOLE);//设置输出模式 6 springApplication.run(DemoApplication.class, args); 7 } 8 } springApplication.setBannerMode方法用于设置banner的输出模式,该方法需要一个Model类型的参数,如下所示: 1 public interface Banner { 2 void printBanner(Environment environment, Class<?> sourceClass, PrintStream out); 3 enum Mode { 4 OFF, 5 CONSOLE, 6 LOG 7 } 8 } 1.3 原理分析 由于springboot中的源码比较复杂,因此我们讲解的时候采取分模块拆解的方式去学习,而不是全盘托出。对于需要讲解的我们要重点关注,对于自己暂时还不熟悉的,我们就留到后面的章节再去单独讲解。这样就可以尽量避免在源码中跟踪来跟踪去,最后不知所云了。 1.3.1 banner类的架构 上述我们看到了banner的三种输出模式,那么问题来了,这些不同的输出模式框架是怎么处理的呢?banner内容如何进行定义呢?banner的定义以及内容有没有限制呢?带着这些问题,我们先看下banner类的顶级接口Banner(org.springframework.boot.Banner)的核心方法如下所示: 1 public interface Banner { 2 void printBanner(Environment environment, Class<?> sourceClass, PrintStream out); } Banner类中只有printBanner(打印Banner)一个方法,该方法顾名思义就是将Banner打印到指定的流中(前面我们也提到过、可以是文件也可以是控制台)。这个接口的实现类如下图所示: 1 Banners:该类内部持有一系列的Banner处理类集合,并在打印流的时候,循环遍历所有的Banner处理类集合进行处理。 2 ImageBanner:打印图片形式的Banner。 3 PrintedBanner:负责装饰其他的Banner类,并内部进行调用。这个后续再详细讲解,现有一个印象即可。 4 ResourceBanner:从一个文本资源中获取需要打印的Banner并输出。 5 SpringBootBanner:默认的Banner打印类。(默认的) 好了,接下来看一下Banner类的初始化以及内部处理逻辑吧。 首先,我们看一下SpringApplication类中的run(String... args)方法,如下所示: 暂时将精力放置到printBanner方法的执行逻辑中,该方法如下所示: private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter( resourceLoader, this.banner); if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); } 上述函数的处理逻辑如下: 1、首先判断banner的输出级别。如果禁用了,则直接返回空。关于日志的输出级别可以参考上文的讲解。 2、获取资源加载器ResourceLoader 。ResourceLoader我们也是可以自定义的,如果你觉得有必要。资源加载器主要加载项目或者jar包中定义好的资源。因为spring将所有的配置文件或者属性文件抽象为资源Resource,所以资源加载器的目的就是加载指定的配置文件或者属性文件并将其封装为Resource实例对象。 3、实例化SpringApplicationBannerPrinter 类,注意一点:this.banner我们是可以自定义的,操作方式如下: SpringApplication springApplication=new SpringApplication(); springApplication.setBannerMode(Banner.Mode.CONSOLE); Resource resource=new ClassPathResource("banner.txt"); springApplication.setBanner(new ResourceBanner(resource)); 上述代码中,我们指定了自己的ResourceBanner并让其加载项目根目录中的banner.txt资源。 注意:spring将项目中的所有配置文件或者属性文件抽象为资源Resource。关于这一点一定要牢记于心。 4、如果banner的输出模式是Mode.LOG,则直接将其信息输出到logger日志中,否则将其输出到控制台,也就是System.out。 logger的定义如下所示: private static final Log logger = LogFactory.getLog(SpringApplication.class); 通过这个地方的处理逻辑可以看出,框架并没有给我们一个自定义的开关来做这个事情。也是写死的方式处理的。 1.3.2 SpringApplicationBannerPrinter类 下面开始重点讲解第三个步骤,也就是SpringApplicationBannerPrinter类的实例化操作。该类的构造函数定义如下所示: 1 private final ResourceLoader resourceLoader; 2 private final Banner fallbackBanner; 3 SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) { 4 this.resourceLoader = resourceLoader; 5 this.fallbackBanner = fallbackBanner; 6 } 在这里,我们暂时记住SpringApplicationBannerPrinter类中持有resourceLoader和fallbackBanner即可。 接下来,我们看一下bannerPrinter.print(environment, this.mainApplicationClass, System.out)这行代码所做的事情,该方法的定义如下所示: 1 public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) { 2 Banner banner = getBanner(environment);//获取Banner 3 banner.printBanner(environment, sourceClass, out);//打印 4 return new PrintedBanner(banner, sourceClass);//实例化PrintedBanner类。 5 } 亦然是三部曲:1、获取Banner;2、调用Banner中的printBanner方法;3、实例化PrintedBanner类。 1.3.2.1. 获取Banner getBanner(environment)方法的定义如下所示: 1 private Banner getBanner(Environment environment) { 2 Banners banners = new Banners(); 3 banners.addIfNotNull(getImageBanner(environment)); 4 banners.addIfNotNull(getTextBanner(environment)); 5 if (banners.hasAtLeastOneBanner()) { 6 return banners; 7 } 8 if (this.fallbackBanner != null) { 9 return this.fallbackBanner; 10 } 11 return DEFAULT_BANNER; 12 } 我们将其执行逻辑进行如下的总结。 1、实例化Banners类,该类内部持有一系列的Banner实例集合。我们可以将其理解为容器。 2、获取ImageBanner、获取TextBanner。 3、如果Banners类中已经包含至少一个Banner实例了,则直接返回,防止Banner太多了。 4、如果Banners类中没有任何Banner实例,则判断fallbackBanner是否为空,如果不为空,则直接返回。注意:这个Banner我们可以自定义,前面已经讲解了,相信大家还有一定的印象。 5、如果fallbackBanner为空,则表示系统内置的一系列Banner没找到,fallbackBanner用户也没有定义,那就没办法了,直接返回默认的Banner,也就是SpringBootBanner。 1.3.2.2. 获取ImageBanner 接下来一个个的看吧。首先看一下getImageBanner的逻辑。该方法的实例代码如下所示: 1 static final String BANNER_LOCATION_PROPERTY = "banner.location"; 2 static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location"; 3 static final String DEFAULT_BANNER_LOCATION = "banner.txt"; 4 static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" } 5 private Banner getImageBanner(Environment environment) { 6 String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY); 7 if (StringUtils.hasLength(location)) { 8 Resource resource = this.resourceLoader.getResource(location); 9 return (resource.exists() ? new ImageBanner(resource) : null); 10 } 11 for (String ext : IMAGE_EXTENSION) { 12 Resource resource = this.resourceLoader.getResource("banner." + ext); 13 if (resource.exists()) { 14 return new ImageBanner(resource); 15 } 16 } 17 return null; 18 } 1、首先,通过environment获取banner.image.location变量的值,在这里如果对于environment不太了解的小伙伴不要担心,这个后续会详细的讲解,我们在这里只需要知道可以通过environment获取到所有的属性信息即可,比如可以通过environment获取到application.properties文件中所有的属性以及值。 2、如果banner.image.location变量存在并且路径是正确的,则直接实例化ImageBanner类并返回,否则开始获取banner.gif、banner.jpg banner.png。三者只要任意一个存在则直接实例化ImageBanner类并返回。 1.3.2.3. 获取TextBanner getTextBanner(environment)方法的代码如下: 1 static final String BANNER_LOCATION_PROPERTY = "banner.location"; 2 static final String DEFAULT_BANNER_LOCATION = "banner.txt"; 3 private Banner getTextBanner(Environment environment) { 4 String location = environment.getProperty(BANNER_LOCATION_PROPERTY, 5 DEFAULT_BANNER_LOCATION); 6 Resource resource = this.resourceLoader.getResource(location); 7 if (resource.exists()) { 8 return new ResourceBanner(resource); 9 } 10 return null; 11 } getTextBanner核心逻辑可以将其总结如下: 1、获取banner.location值,如果没有配置,则直接使用内置的banner.txt。看到这里貌似明白了,我们默认的行为就是该方式,如果期望打印banner信息,只需要在项目的根目录中建立一个banner.txt文件,并填写相应的信息即可替换默认的输出内容(上文已经提及到)。 2、如果banner.location值不为空,并在其配置的文件存在,则直接实例化ResourceBanner并返回。 关于banner.txt目录结构的配置如下所示: 如果我们需要通过banner.location方式指定,则可以直接在application.properties文件中进行如下的设置: banner.location=banner.txt。当然了这种方式的话banner.txt文件的名称可以自定义。 本文我们暂时先讲解到这里,关于一系列的banner子类,我们下一篇文章一个个的讲解。大家有兴趣的话可以自行跟踪一下。 欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 扫一扫下方二维码或者长按识别二维码,即可关注。 作者:分享牛 出处:http://blog.csdn.net/qq_30739519 本博客中未标明转载的文章归作者分享牛所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
摘要:在使用springboot的时候,可能经常会忽略掉springboot的版本问题。本文我们看一下springboot jar包中定义的版本信息以及版本获取类。本文内容相对而言比较简单。 1.java中定义项目的版本 回想一下在java中如何定义项目的版本。这个比较简单,只需要在jar包增加MANIFEST.MF文件(根目录)并添加的如下内容即可: Manifest-Version: 1.0Implementation-Title: 分享牛Implementation-Version: 1.1 关于MANIFEST.MF文件的位置如下所示: 2.springboot中版本信息 同样,在springboot中spring-boot-2.0.0.M3.jar包中MANIFEST.MF定义了相关的版本信息,具体内容如下所示: Manifest-Version: 1.0 Implementation-Title: Spring Boot Implementation-Version: 2.0.0.M3 Built-By: bamboo Specification-Vendor: Pivotal Software, Inc. Specification-Title: Spring Boot Implementation-Vendor-Id: org.springframework.boot Created-By: Apache Maven 3.5.0 Build-Jdk: 1.8.0_121 Specification-Version: 2.0 Implementation-URL: http://projects.spring.io/spring-boot/ Implementation-Vendor: Pivotal Software, Inc. 3.springboot中版本信息的获取工具类 了解了上述的版本定义之后,我们就开始说明如何进行MANIFEST.MF中定义的版本信息值的获取,可能聪明的小伙伴就想到了,直接读取MANIFEST.MF文件 并解析Implementation-Version属性不就可以了,当然可以使用这种方式,但是我们可以直接利用Springboot框架本身提供的一些的类进行操作了,这个类就是 SpringBootVersion类,该类的定义如下所示: public final class SpringBootVersion { private SpringBootVersion() { } /** * Return the full version string of the present Spring Boot codebase, or {@code null} * if it cannot be determined. * @return the version of Spring Boot or {@code null} * @see Package#getImplementationVersion() */ public static String getVersion() { Package pkg = SpringBootVersion.class.getPackage(); return (pkg != null ? pkg.getImplementationVersion() : null); } } 当我们需要获取springboot中的版本,只需要调用SpringBootVersion类即可。 注意:SpringBootVersion类修饰符是final,所以我们不能定义子类去继承该类。 4.扩展点 SpringBootVersion类位于spring-boot-2.0.0.M3.jar包中,并且版本信息文件MANIFEST.MF位于spring-boot-2.0.0.M3.jar包中,那是不是我们就可以通过 spring-boot-2.0.0.M3.jar包中的任意一个类去获取版本信息呢?答案是肯定的。当然可以获取到的。实例代码如下: public class SpringBootVersionTest { public static void main(String[] args) { String version = SpringBootVersion.getVersion(); System.out.println(version); String implementationVersion = SpringApplication.class.getPackage().getImplementationVersion(); System.out.println(implementationVersion); } } 上述代码中,我们分别通过了SpringBootVersion类以及SpringApplication类进行版本信息的获取。运行上述的代码,控制台输出的信息如下: 2.0.0.M32.0.0.M3
在flowable6.3以后的版本中,支持了MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION事件,这个事件顾名思义就是在多实例节点完成的时候,flowable引擎去发布这个完成事件信号。该事件同其他的事件一样,定义在FlowableEngineEventType.java类中。 关于多实例所支持的事件类型如下所示: MULTI_INSTANCE_ACTIVITY_COMPLETED(多实例完成), MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION(多实例正常完成), MULTI_INSTANCE_ACTIVITY_CANCELLED(多实例取消), 不管是什么事件,我们在开发的过程中往往只会关心事件的类型以及事件所产生的数据而已。现在既然我们知道了新增的事件类型是 MULTI_INSTANCE_ACTIVITY_COMPLETED, MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION, MULTI_INSTANCE_ACTIVITY_CANCELLED, 三个,那接下来,我们看一下这些事件所对应的事件处理类,具体细节在AbstractFlowableEngineEventListener类中如下所示: case MULTI_INSTANCE_ACTIVITY_COMPLETED: multiInstanceActivityCompleted((FlowableMultiInstanceActivityCompletedEvent) flowableEngineEvent); break; case MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION: multiInstanceActivityCompletedWithCondition((FlowableMultiInstanceActivityCompletedEvent) flowableEngineEvent); break; case MULTI_INSTANCE_ACTIVITY_CANCELLED: multiInstanceActivityCancelled((FlowableMultiInstanceActivityCancelledEvent) flowableEngineEvent) protected void multiInstanceActivityCompleted(FlowableMultiInstanceActivityEvent event) {} protected void multiInstanceActivityCompleted(FlowableMultiInstanceActivityCompletedEvent event) {} protected void multiInstanceActivityCompletedWithCondition(FlowableMultiInstanceActivityCompletedEvent event) {} protected void multiInstanceActivityCancelled(FlowableMultiInstanceActivityCancelledEvent event) {} MULTI_INSTANCE_ACTIVITY_COMPLETED事件对应的事件类为:FlowableMultiInstanceActivityCompletedEvent MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION事件对应的事件类为:FlowableMultiInstanceActivityCompletedEvent。 MULTI_INSTANCE_ACTIVITY_CANCELLED事件对应的事件类为:FlowableMultiInstanceActivityCancelledEvent。由上面的源码可以知道 MULTI_INSTANCE_ACTIVITY_COMPLETED与MULTI_INSTANCE_ACTIVITY_COMPLETED_WITH_CONDITION事件对应的事件类是相同的,都是FlowableMultiInstanceActivityCompletedEvent。 MULTI_INSTANCE_ACTIVITY_CANCELLED事件对应的事件类为:FlowableMultiInstanceActivityCancelledEvent。 通过FlowableMultiInstanceActivityCompletedEvent类我们可以获取到的属性信息如下: numberOfInstances(多实例的个数) numberOfActiveInstances(多实例活动的个数) numberOfCompletedInstances(多实例已经完成的活动个数) 关于更多的事件以及事件监听器可以参考Activiti权威指南一书 。
摘要:在实际项目开发中使用springboot的时候,可以使用jar包的方式运行项目,也可以将springboot项目打成war包使用。springboot war包运行可能会出现 [localhost-startStop-1] DEBUG org.springframework.jndi.JndiLocatorDelegate - Converted JNDI name [java:comp/env/LOGGING.pattern_level] not found - trying original name [LOGGING.pattern_level]. javax.naming.NameNotFoundException: Name [LOGGING.pattern_level] is not bound in this Context. Unable to find [LOGGING.pattern_level] 。反正就是诸如此类的问题吧。 上述的问题,在高版本的spring boot中会出现的,低版本不会出现这个问题。因为高版本中引入了JNDI查询的操作。 解决方案: 在项目的根目录中新建spring.properties配置文件,如下图所示: 添加属性以及值,如下所示: spring.jndi.ignore=true 原理 打开StandardServletEnvironment类,该类中的customizePropertySources方法如下: public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties"; protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } super.customizePropertySources(propertySources); } JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()代码如下: public static final String IGNORE_JNDI_PROPERTY_NAME = "spring.jndi.ignore"; private static final boolean shouldIgnoreDefaultJndiEnvironment = SpringProperties.getFlag(IGNORE_JNDI_PROPERTY_NAME); public static boolean isDefaultJndiEnvironmentAvailable() { //如果忽略jndi则返回false if (shouldIgnoreDefaultJndiEnvironment) { return false; } try { //准备jndi环境 new InitialContext().getEnvironment(); return true; } catch (Throwable ex) { return false; } } 通过上文可知: 1、如果我们需要忽略jndi则可以配置spring.jndi.ignore值为true即可。 2、在哪里配置呢?我们不妨跟进SpringProperties类中的getFlag方法。 spring.jndi.ignore获取原理 SpringProperties类的getFlag方法如下所示: public static boolean getFlag(String key) { return Boolean.parseBoolean(getProperty(key)); } 继续跟进getProperty方法,如下所示: public static String getProperty(String key) { //获取spring.jndi.ignore值 String value = localProperties.getProperty(key); if (value == null) { try {//获取系统的变量 value = System.getProperty(key); } catch (Throwable ex) { } } return value; } 上述的方法可以总结如下: 从localProperties集合中获取spring.jndi.ignore属性,如果没有获取到则直接从环境变量中进行获取。localProperties集合在哪里初始化的呢?我们看一下SpringProperties类中的静态代码块,如下所示: private static final String PROPERTIES_RESOURCE_LOCATION = "spring.properties"; static { try { ClassLoader cl = SpringProperties.class.getClassLoader(); URL url = (cl != null ? cl.getResource(PROPERTIES_RESOURCE_LOCATION) :ClassLoader.getSystemResource(PROPERTIES_RESOURCE_LOCATION)); if (url != null) { logger.info("Found 'spring.properties' file in local classpath"); InputStream is = url.openStream(); try { localProperties.load(is); } finally { is.close(); } } } catch (IOException ex) { } } 看到上面的代码,我们明白了,原来这里是直接读取跟目录中的spring.properties文件中的所有属性。看到这里,焕然大悟。原来如此。 ok,本文暂时讲解到这里。后续我们讲解更多的jndi使用。
近期在研究jvm原理,看了不少书,总感觉停留在理论上,不能系统的学习以及深入理解。以及正所谓“纸上得来终觉浅,绝知此事要躬行”,要想深入的了解jvm原理,最起码要手动编译一个jdk以及虚拟机玩玩。本文总结一下自己在cenos6中编译openjdk8的一些粗浅经验,供大家参考学习。 1.操作系统说明 本文使用的cenos系统版本为cenos6.4 64位。可以使用如下的命令进行查看。 [root@localhost test]# uname -a Linux localhost 2.6.32-358.el6.x86_64 #1 SMP Fri Feb 22 00:31:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux 2.编译 2-1:获取源码 在本次的openjdk编译中,我们使用的版本为openjdk8.因此我们需要获取到openjdk8的源码。 openjdk8源码的获取方式有两种方式 1.使用hg工具进行源码的下载。 2.直接在官网进行下载源码包(下载地址)。 2-1-1 hg下载openjdk源码。 这种方式获取源码,比较耗时,下载的耗时时间,完全取决与我们的网速。在这里还是简单说明一下: 首先,我们需要下载Mercurial。详细步骤如下: cd /var/local/ wget http://mercurial.selenic.com/release/mercurial-3.0.tar.gz tar xvzf mercurial-3.0.tar.gz mv mercurial-3.0 mercurial cd mercurial make all make install 上述的命令执行完毕之后,如果没有错误,则输入hg -v命令之后,控制台显示的信息如下: 分布式软件配置管理工具 - 水银 基本命令: add add the specified files on the next commit annotate, blame show changeset information by line for each file clone make a copy of an existing repository commit, ci commit the specified files or all outstanding changes diff diff repository (or selected files) export dump the header and diffs for one or more changesets forget forget the specified files on the next commit init create a new repository in the given directory log, history show revision history of entire repository or files merge merge another revision into working directory pull pull changes from the specified source push push changes to the specified destination remove, rm remove the specified files on the next commit serve start stand-alone webserver status, st show changed files in the working directory summary, sum summarize working directory state Mercurial安装完毕之后,我们就开始下载openjdk8的源码,命令如下: hg clone http://hg.openjdk.java.net/jdk8u/jdk8u jdk8u cd jdk8u bash get_source.sh经过一段耐心时间的等待,源码就会下载完毕。如果网速不给力的小朋友,可以直接通过我提供的下载地址下载即可。 3、openjdk8源码安装 3.1 安装jdk必备软件包 安装jdk必备软件包如下所示: yum makecache yum -y groupinstall 'base' yum groupinstall "Development Tools" yum install libXtst-devel libXt-devel libXrender-devel yum install cups-devel yum install freetype-devel yum install alsa-lib-devel 大家可以一个个的下载安装即可。 3.2 环境准备 上述的一个个软件包安装完毕之后,我们来开始配置环境。首先我们看一下openjdk-jdk8u-jdk8u中的README-builds.htm文件,这个文件中详细说明了构建openjdk8的步骤以及方法,由于是英文版的,所以在这里我们暂时不一个个的说明了,用到的命令我们再来详细的说明。在构建环境之前,还是简单看一下这个文件,如下: Some Headlines: The build is now a "configure && make" style build Any GNU make 3.81 or newer should work The build should scale, i.e. more processors should cause the build to be done in less wall-clock time Nested or recursive make invocations have been significantly reduced, as has the total fork/exec or spawning of sub processes during the build Windows MKS usage is no longer supported Windows Visual Studio vsvars*.bat and vcvars*.bat files are run automatically Ant is no longer used when building the OpenJDK Use of ALT_* environment variables for configuring the build is no longer supported 上面的一段话,大体意思就是openjdk8源码的构建可以直接使用config && make方式。换言之,openjdk8之前的额源码构建需要使用Ant和ALT_ *。需要使用到的make版本为3.81以及以后的版本(包含)。 3.3 make版本检查 在控制台输入make -v命令,输出信息如下: [root@localhost openjdk-jdk8u-jdk8u]# make -v GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program built for x86_64-redhat-linux-gnu 3.4 引导启动jdk设置 注意:在编译openjdk8的时候,我们需要安装一个引导启动的jdk,并且这个jdk必须是7版本。这一点需要注意,引导启动jdk的版本要比编译的jdk版本小1。比如我们在编译openjdk7,那么我们需要安装的引导启动jdk版本为6;我们在编译openjdk6,那么我们需要安装的引导启动jdk版本为5。 在这里我使用的引导启动jdk版本为jdk1.7.0_80ï¼ç®å½ä¸º/opt/jdk1.7.0_80。 4、开始编译openjdk8 上述的环境准备完毕之后,就开始我们的openjdk8编译了。首先我们进入/opt/openjdk-jdk8u-jdk8u目录,输入的命令如下: cd openjdk bash ./configure --with-target-bits=64 --with-boot-jdk=/opt/jdk1.7.0_80 --with-debug-level=slowdebug --enable-debug-symbols ZIP_DEBUGINFO_FILES=0 make all ZIP_DEBUGINFO_FILES=0 参数说明: --with-boot-jdk:指定引导JDK所在目录,以防其他安装的JDK影响(本机上以前安装了JDK8,并配置了JAVA_HOME指向JDK8); --with-target-bits:指定编译64位系统的JDK; 为可以进行源码调试,再指定下面三个参数: --with-debug-level=slowdebug:指定可以生成最多的调试信息; --enable-debug-symbols ZIP_DEBUGINFO_FILES=0:生成调试的符号信息,并且不压缩;这个调试的时候非常有用。 命令最终执行完毕之后,即可完成编译,因为我实在虚拟机中进行openjdk8的编译,所以大概花费了2个小时。 5.测试编译的openjdk 编译的文件位于build/linux-x86_64-normal-server-slowdebug中,我们继续进入子目录,完整目录如下: /opt/openjdk-jdk8u-jdk8u/build/linux-x86_64-normal-server-slowdebug/jdk/bin 输入java -version命令,效果如下所示: [root@localhost bin]# java -version java version "1.8.0_162-ea" Java(TM) SE Runtime Environment (build 1.8.0_162-ea-b01) Java HotSpot(TM) 64-Bit Server VM (build 25.162-b01, mixed mode) ok,当看到上面的命令成功执行之后,便说明我们的openjdk8手工编译成功了。
课程地址:http://www.shareniu.com/list.htm行为类:链接:http://pan.baidu.com/s/1sljSTcD 密码:uw5a目录:第1课:行为类课程概览第2课:什么是行为类以及什么是行为工厂类和他们的作用第3课:活动行为工厂类初始化原理。第4课:自定义活动行为工厂类以及演示任务行为类第5课:行为类使用的误区以及通过任务节点行为类动态增加处理人第6课:活动行为类整体架构第7课:排它网关实现原理以及演示错误第8课:按照项目需求改造排它网关,是其更业务化第9课:忽略节点使用以及实际项目中如何灵活改造,自动跳过节点的设计代码演示第10课:忽略节点使用使用误区
activiti源码分析
如何解决Activiti-modeler无法使用的问题 之前学习Activity的时候在网上面找了各种关于modeler使用说明,但是往往必须非常严格的按照教程的路线进行使用。 一、了解modeler需要些什么才能够正常运作 首先就是将activity框架搞定,如果连这个都没有的话也就无法运转。 其次我们需要引入一些jar包,具体可以参考其他文章使用的全部jar包下面只列出一个关键的。 下面一个ModelEditorJsonRestResource文件和ModelSaveRestResource文件就是关键了 两个文件提供了两个modeler需要的接口,一个负责保存一个负责获取(通过JSON格式) 接着根据网上所说把modeler需要的html文件都拷到项目里面来 放好之后我们怎么能够让modeler知道save和json接口是多少呢? 所以我们首先需要找到接口的具体位置,这里使用的是IDEA加上TOMCAT所以在部署的时候会列出项目的接口 我们直接访问json链接试试行不行 本文章采用的根地址是http://localhost:8080/service 访问全地址是http://localhost:8080/service/model/35014/json 证实了下确实是可以使用的我们只需要静态的访问modeler.html然后传modelID参数即可 例如http://localhost:8080/modeler.html?modelId=35014 但是整好之后却发现还是不能用用谷歌游览器进入开发者模式之后看到 以下由群友B-Romantic提供 http://localhost:8080/service/model//json Failed to load resource: the server responded with a status of 404 (Not Found) 那么就是访问的地址不对,我们需要修改 里面的app-cfg.js来调整路径(默认是/) 后面多加一个/就会出现上述错误,我们再测试以下 http://localhost:8080/modeler.html?modelId= 加上model 的ID看是否能够解决。 以上就是我个人总结出来的解决经验,如有不足请指正。
flowable 6.0.0版本于2017年2月15日正式发布了。 关于flowable的几篇文章如下: flowable正式版 flowable appModel flowable节点撤销 flowable-rest使用 ad-hoc子流程使用 flowable-task使用 flowable使用 flowable使用 flowable使用
flowable增加了App部署,这样我们在实际的项目开发中就可以为部署的流程资源绑定一个app的主题和图标。 看起来挺酷的样子。那么这个功能该如何使用呢?flowable在设计之初为何需要添加这样的功能。 关于 flowable appModel的使用可以参考http://www.shareniu.com/article/34.htm一文。
Activiti6最大的变化点就是对代码进行了重构,该版本修复以往的Bug并不多,但内部实现相对来说变化比较大。 更多的变化点可以访问网站http://www.shareniu.com/article/30.htm
flowable和Activiti6新增了ad-hoc子流程(adHocSubProcess),即adHocSubProcess流程。该类型的子流程无需再子流程中为节点配置任何的出线: 操作方式如下: runtimeService.getEnabledActivitiesFromAdhocSubProcess(executionId); runtimeService.executeActivityInAdhocSubProcess(executionId, id); runtimeService.completeAdhocSubProcess(executionId); <adHocSubProcess id="adhocSubProcess" ordering="Sequential"> <userTask id="subProcessTask" name="Task in subprocess" /> <userTask id="subProcessTask2" name="Task2 in subprocess" /> <completionCondition>${completed}</completionCondition> </adHocSubProcess> 关于ad-hoc子流程的更多使用可以参考http://www.shareniu.com/article/25.htm
上文讲解了如何启动和flowable-task模块。本文重点讲解一下flowable-task模块提供的操作。flowable-task模块用于动态的生成任务、完成任务、转办任务、指派任务、对任务进行评论、指定任务的到期时间等。 接下来一睹为快吧。 创建任务 flowable-task模块创建任务的操作如下图所示: 点击create-task按钮,弹出如下的界面: 上图中 Name为任务的名称,本文的名字为http://www.shareniu.com/。 Description:任务的描述信息,本文的名字为分享牛,分享牛。 点击create按钮即可完成任务的创建工作。 act_ru_task表新增的数据如下图所示: 新增的数据的操作演示完毕。 指定任务处理人 上文中使用的admin账户登录的,因此上述新增的任务处理人默认为admin,现在讲该任务交给shareniu进行处理,操作如下: 点击上图中的 Involve,然后输入期望将该任务指定的具体处理人,如下图所示: 点击选中shareniu即可完成该操作,act_ru_identitylink表新增的数据如下图所示: 其他的操作可集合该操作观察数据库中表相应的变化即可。 原文地址:http://www.shareniu.com/article/20.htm 更多优质文章,请关注分享牛http://www.shareniu.com/
flowable 新增了idm模块,主要用于管理flowable-admin、flowable-idm、flowable-modeler、flowable-rest、flowable-task等模块。 flowable idm用于管理 的表如下: act_id_priv、act_id_group、act_id_membership、act_id_priv_mapping、act_id_property、act_id_token、act_id_user。 其中act_id_priv表的数据如下: mysql> select * from `act_id_priv`;+--------------------------------------+----------------+| ID_ | NAME_ |+--------------------------------------+----------------+| a8e8dd8e-e5ea-11e6-9f5a-30b49e08e9bc | access-idm || a8f42830-e5ea-11e6-9f5a-30b49e08e9bc | access-admin || a90c4412-e5ea-11e6-9f5a-30b49e08e9bc | access-modeler || a92c4f34-e5ea-11e6-9f5a-30b49e08e9bc | access-task |+--------------------------------------+----------------+access-idm 对应 flowable-idm模块 access-admin 对应 flowable-admin模块 access-modeler 对应flowable-modeler模块 access-task对应flowable-task模块。 act_id_priv_mapping表对应哪些用户或者组可以访问以上所述的四个模块,可以将其理解为控制人可以访问的资源。 act_id_token表用于记录何时何地访问了系统,类似日志记录。 act_id_user表:用户表。存放用户名或者密码信息。 act_id_group:存放了组的信息。 act_id_info:用户的详细信息。 act_id_membership:用户与组的中间表。 首先,将flowable-idm.war放置到tomcat(本文的tomcat本地地址为C:\sottware\apache-tomcat-7.0.73-windows-x86\apache-tomcat-7.0.73)的webapp目录中,然后将其解压。 由于flowable idm 默认使用的是H2数据库,为了方便使用,本文以MySql为例。 首先将mysql-connector-java-5.1.35.jar包放置到C:\sottware\apache-tomcat-7.0.73-windows-x86\apache-tomcat-7.0.73\webapps\flowable-idm\WEB-INF\lib目录中, 然后修改C:\sottware\apache-tomcat-7.0.73-windows-x86\apache-tomcat-7.0.73\webapps\flowable-idm\WEB-INF\classes\META-INF\flowable-idm-app中的flowable-idm-app.properties内容为如下所示: # # SECURITY # security.rememberme.key=testkey # # DATABASE #分享牛原创,更多优质文章请访问http://www.shareniu.com/ datasource.driver=com.mysql.jdbc.Driver datasource.url=jdbc:mysql://127.0.0.1:3306/flowable-idm?useUnicode=true&characterEncoding=utf-8 datasource.username=root datasource.password= # # DEFAULT ADMINISTRATOR ACCOUNT #分享牛原创,更多优质文章请访问http://www.shareniu.com/ admin.email=admin admin.password=test admin.firstname=Test admin.lastname=Administrator 其中admin为用户名,密码为test。 上述工作完毕之后,启动tomcat,如果启动成功则如下图所示: 然后输入用户名和密码,如下图所示:
在使用flowable框架的时候,首先需要引入flowable的jar包,flowable maven仓库地址为:<!-- https://mvnrepository.com/artifact/org.flowable/flowable-engine --><dependency> <groupId>org.flowable</groupId> <artifactId>flowable-engine</artifactId> <version>6.0.0.RC1</version></dependency> 新建flowable.cfg.xml文件,如下图所示: flowable.cfg.xml文件内容如下所示: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--分享牛http://www.shareniu.com/ --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://127.0.0.1:3306/shareniuflowable?useUnicode=true&characterEncoding=UTF-8 </value> </property> <property name="username"> <value>root</value> </property> <property name="password" value="" /> <!-- --> </bean> <!--分享牛http://www.shareniu.com/ --> <bean id="processEngineConfiguration" class="org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration" > <property name="dataSource" ref="dataSource" /> <property name="databaseSchemaUpdate" value="true" /> </bean> </beans> 新建测试类如下所示: package com.shareniu.flowables.ch1; import java.io.IOException; import java.io.InputStream; import org.flowable.engine.IdentityService; import org.flowable.engine.ProcessEngine; import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.DeploymentBuilder; import org.junit.Before; import org.junit.Test; /** * 分享牛http://www.shareniu.com/ * */ public class App { // 获取到Activiti ProcessEngine ProcessEngine processEngine = null; // 获取RepositoryService 实例对象 RepositoryService repositoryService = null; // 资源名称 String resourceName = "shareniu_addInputStream.bpmn"; IdentityService identityService; RuntimeService runtimeService; TaskService taskService; @Before public void init() { InputStream in = App.class.getClassLoader().getResourceAsStream( "com/shareniu/flowables/ch1/flowable.cfg.xml"); ProcessEngineConfiguration pcf = ProcessEngineConfiguration .createProcessEngineConfigurationFromInputStream(in); processEngine = pcf.buildProcessEngine(); repositoryService = processEngine.getRepositoryService(); identityService = processEngine.getIdentityService(); runtimeService = processEngine.getRuntimeService(); taskService = processEngine.getTaskService(); ProcessEngineConfigurationImpl pc = (ProcessEngineConfigurationImpl) processEngine .getProcessEngineConfiguration(); } @Test public void addInputStreamTest() throws IOException { // 定义的文件信息的流读取 分享牛http://www.shareniu.com/ InputStream inputStream = App.class .getClassLoader().getResource("com/shareniu/flowables/ch1/common.bpmn").openStream(); // 流程定义的分类 分享牛http://www.shareniu.com/ String category = "shareniu_addInputStream"; // 构造DeploymentBuilder对象 DeploymentBuilder deploymentBuilder = repositoryService .createDeployment().category(category) .addInputStream(resourceName, inputStream); // 部署 Deployment deploy = deploymentBuilder.deploy(); System.out.println(deploy); } } 运行上述代码,流程文档以及成功部署。
原文地址:www.shareniu.com/topic/content/40.htm Activiti自定义设计器 在使用Activiti的时候,需要绘制流程文档,可以使用Activiti提供的Eclipse插件或者idea插件,抑或使用Web版的流程设计器,但是这些设计器不能很好的满足业务开发需求,因此通常情况下,在实现自己业务的同时,期望自己设计一套流程设计器,从而更加灵活的应对需求的变更和量身定制自己的Activiti设计器。下面是系统截图说明: 版本归http://www.shareniu.com/所有,未经授权请不要转载,否则追究法律责任。
原文地址:http://www.shareniu.com/ 工欲善其事必先利其器,要想使用flowable,必须搭建一套环境,本文以Eclipse中安装flowable插件为例详细说明整个安装过程。 首先,打开Eclipse,HelpàInstall New Software.然后输入一下信息: · Name: Flowable BPMN 2.0 designer · Location: http://flowable.org/designer/update/ 经过一段时间就可以安装成功了。 如果成功,则打开Eclipse,新建一个flowable项目,如下图所示: 版本归http://www.shareniu.com/所有,未经授权请不要转载,否则追究法律责任。
Flowable 6.0.0.RC1 release,第一个可流动的6引擎版本(6.0.0.RC1)。 Flowable 6.0.0.RC1 relase新增加的功能以及特色: 包重命名为org。Flowable ,重命名flowable.cfg的配置文件。xml和flowable-context.xml。 类名称重命名使用Flowable 而不是Activiti在需要的地方。 功能在IDM引擎分离,身份和身份数据库表是由这个引擎。默认情况下IDM引擎启动时启用可流动的引擎,但它可以被禁用的disableIdmEngine。 介绍的内容引擎,它提供了一个简单的文件存储附加文件/文档任务或流程实例。 支持非中断事件子流程。直到这个版本只支持中断事件子过程,但是现在可以处理事件,让主要流程执行下去。 任务的应用程序被重构为三单独的web应用程序。有一个可流动的Modeler包含建模应用程序存储库功能+ BPMN,形式和静编辑器。第二个应用程序是可流动的IDM应用,管理用户和组,并为每一个应用程序处理身份验证。例如可流动的Modeler将重定向到易流动的IDM应用程序时没有找到有效的饼干。登录后可流动的IDM应用程序,执行重定向到易流动的Modeler Modeler可以访问存储库。这样做是为了让不同的应用程序之间的单点登录。第三个应用程序是可流动的任务应用程序,允许您启动流程实例和工作任务和填充表单。 引入可流动的管理应用程序,它允许查询BPM,静,形式和内容引擎。 在Hibernate持久性逻辑是所有被重写MyBatis为所有持久性逻辑有一个共同的框架。 各种和许多小错误修正。 Flowable
分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) Bug [ACT-1968] - Custom Database schema on PostgreSQL not working [ACT-4113] - REST Task API Not working: /service/runtime/tasks?assigneeLike=XX and service/runtime/tasks?ownerLike=YY [ACT-4193] - Bug in 'Like' expressions in Activiti Rest? [ACT-4199] - DefaultDeploymentCache uses non thread safe HashMap [ACT-4212] - Mutating list variable for users in parallel multi instance tasks causes floating executions Task [ACT-4210] - Add category as query param and json field for Task Rest API Improvement [ACT-4074] - Activiti Spring Boot REST API starter does not use IdentityServiceUserDetailsService [ACT-4206] - Webservice endpoint address should be customizable Release Notes - Activiti - Version 5.21.0 Highlights This release contains some quite important bugfixes, more specifically it fixes some cases where the end time was not set for activities in a process definition under certain conditions. A concurrency bug was discovered when using delegateExpressions together with field injection. Make sure to read the updated documentation section on the use of 'delegateExpression'. Included in this release is the 'secure scripting' feature (for javascript), which allows to execute scripts in a secure way. You can find the documentation in the 'advanced' section of the user guide. All changes: Bug [ACT-2081] - taskCandidateOrAssigned doesn't work with custom GroupEntityManager and UserEntityManager [ACT-4090] - Boundary timer with fixed number of repeats fires all events at once when attached to nested activity [ACT-4152] - Task Category not included in query result under MS SQL Server 2012 [ACT-4171] - End time incorrectly set on multi-instance subprocess historic activity [ACT-4172] - DelegateExpression usage not thread-safe [ACT-4176] - End time not correctly set on historic activity [ACT-4185] - NullpointerException occured on referring HistoricVariableUpdate(Serializable Type) [ACT-4186] - ConcurrentModificationException when event listener removes itself [ACT-4189] - Process diagram generator: support multiple pools [ACT-4190] - Job with null due date is never picked up again [ACT-4181] - fix or queries containing processDefinitionKey clause eliminated adhoc tasks from result set [ACT-4184] - Fetching historic task with task local and process variables returns current task variables New Feature [ACT-4166] - Support for multiple signal start events [ACT-4167] - Support for multiple timer start events [ACT-4177] - Secure scripting feature [ACT-4179] - Allow to set business calendar on timer event definition [ACT-4180] - Process diagram generator: allow to change font for annotations [ACT-4182] - Query historic variables with a list of execution/task ids [ACT-4183] - Explorer: omitting the protocol (scheme) in order to preserve the protocol of the current page [ACT-4187] - Make variable limit configurable when fetching variables [ACT-4188] - Add support for associations in auto layout and diagram generator Improvement [ACT-2146] - Consider switching around the firing of TaskListeners and ActivitiEventListeners Release Notes - Activiti - Version 5.20.0 Highlights This is a bugfix release (mainly fixing the bug around losing message and signal start event subscriptions on process definition redeployment, see ACT-4117). Bug [ACT-2163] - Fix some bugs and optimize display on text in workflow drawing [ACT-4051] - Spring Boot support does not discover processes in external JARs [ACT-4097] - Subprocess start events not present in historic activity instance list [ACT-4101] - NPE when there are multiple routes to terminateEndEvent, and both are reached [ACT-4109] - Database exception when using both processInstanceId and executionId on HistoricVariableInstanceQuery [ACT-4112] - Subscribed Event Deleted after the related process is redeployed (MySQL database) [ACT-4117] - Signal and Boundary event subscription are lost when deploying a new process definition version [ACT-4118] - Signal and Message start event are not restored when deleting the latest version of a deployment [ACT-4119] - Stale ProcessDefinition cache when modifying the suspensionState [ACT-4130] - Unused assignment and possible integer overflow [ACT-4131] - Message events are not restored after deployment for processes with tenant id [ACT-4137] - Historic process instance endTime not set when using terminate end event Task [ACT-4091] - Tasks list empty in executionEntity [ACT-4104] - Boundary message event isn't exported Improvement [ACT-4064] - Missing some search filters in "Get historic task instances" REST service Release Notes - Activiti - Version 5.19.0 Highlights We introduced a new service DynamicBpmnService that allows you to change a defined set of properties of a process definition without needing to redeployhttp://bpmn20inaction.blogspot.com/2015/10/adding-dynamic-behaviour-to-activiti.html Improved support for the terminate end event. According to the BPMN spec a terminate end event should only terminate the current scope. So in case of a multi instance sub process, a terminate end event inside that multi instance sub process should only kill a single instance and the rest of the instances (of the multi instance) will continue. And in case a call activity kicks off a sub process, a terminate end event in the sub process will only kill the sub process instance, and the parent process will continue. We've added a new attribute on the terminate end event (activiti:terminateAll) that allows you to terminate all parent scopes if the value is set to true. By default only the current scope is killed. ACT-4072 was fixed, a repeating timer (time cycle) now works according to the defined duration values Various bug fixes Check out the Release notes for more details Bug fixes and various smaller improvements Bug [ACT-1768] - Revision of same variable is always zero [ACT-2129] - Activiti can't store string variables with length >2000 and <4000 characters in oracle db [ACT-2163] - Fix some bugs and optimize display on text in workflow drawing [ACT-3997] - Validate bpmn components for max length [ACT-4066] - Timer Start Event ignores Time Cycle value if iteration count not present [ACT-4069] - Activiti Spring Boot REST API starter incompatible with Spring Boot HATEOAS starter [ACT-4079] - NullPointerException in DbSqlSessionFactory.isBulkInsertable() when Custom Entity is inserted [ACT-4084] - Multi instance parallel task fails with empty collection New Feature [ACT-4010] - Design tools should support message artifact - what is the runtime implementation for registering custom messages? Task [ACT-4045] - enable shared process definitions per tenants [ACT-4072] - Repeating timer with no repetition bound does not follow duration [ACT-4080] - Verify terminate end behavior Release Notes - Activiti - Version 5.18.0 Highlights We introduced bulk inserts. For more information you can read more about it in this blog post http://www.jorambarrez.be/blog/2015/07/20/the-activiti-performance-showdown-2015/ We enhanced the OR query support, it's now possible to use multiple OR parts in one query Improvements to the async executor we introduced in Activiti 5.17.0. There were some cases that could result in deadlock exceptions and that's resolved in this version. Improvements to the Activiti Modeler with terminate end events, cancel event and transaction support, and more intermediate catch and throw event support. Also, improvements to interrupting and non-interrupting solid and dashed line for boundary events. Upgraded Spring dependency version to 4.1. Activiti is still working with Spring 4.0.x and 3.x as well Various bug fixes Known issues There's a known issue with Chrome and the Activiti Explorer / Vaadin 6. You can get a blank screen in the details part of the Explorer. This is a known issue for Vaadin 6 and Chrome 43 and 44. There are bug fixes under way with Chrome 45, so that might fix it. Other browsers like Firefox work fine. Backwards incompatible changes https://activiti.atlassian.net/browse/ACT-2186 : When variables are removed, the historic variable entry is also removed. In earlier versions, the value of the historic variable was set tonull. https://activiti.atlassian.net/browse/ACT-4036 : when using an execution listener for the start event, in combination with multi instance, the behavior is changed. In 5.18, there will be one extra invocation of the execution listener (when entering the multi instance, without the loopCounter variable being said). For more information, check the section Multi instance and execution listeners in the user guide. Bug fixes and various smaller improvements Check out the Release notes for more details Bug [ACT-2100] - Compensation event endTime is not logged [ACT-2173] - Add support of import relative to the BPMN description [ACT-2175] - NPE when retrieving a local variable with a null value multiple times [ACT-2176] - SkipExpression support is not completing skipped user tasks [ACT-2179] - unable to use Date process variable in timeDuration [ACT-2182] - Historic process instance query is missing columns for MS SQL [ACT-2185] - Unexpected behavior calling a suspended process as a called activity [ACT-2186] - Historic variable is not removed when variable is removed [ACT-2188] - New Modeler - Missing "cancelActivity" in the BoundaryMessageEvent [ACT-2194] - NPE when using parallel multi-instance on embedded subprocess [ACT-2195] - Int type for data object not working in Activiti Designer [ACT-2198] - Exporting/importing model changes name of Message [ACT-2199] - Boundary Signal Event ignores "cancel activity" flag [ACT-2203] - Fix potential null pointer deferences and other [ACT-2210] - Add support for terminate event in Modeler [ACT-2217] - JPAEntityScanner / EntityMetaData cannot deal with Id methods defined in generic super classes [ACT-2219] - Wrong redirected URI when converting a deployed process to editable model [ACT-2220] - An enum type form property in model designed in Activiti modeler does not show during execution [ACT-2221] - Admin completed instances > show variable id instead of name [ACT-2231] - type ENUM not supported in Activti 5.17 [ACT-2232] - Activiti REST not support Groovy script [ACT-2236] - Import of process model corrupts boundary events [ACT-3999] - ClassCastException when importing a WSDL [ACT-4001] - CxfWsdlImporter does not import correctly inherited types [ACT-4011] - Duplicate event when deleting a candidate group [ACT-4012] - IllegalArgumentException when invoking a service task requiring a date [ACT-4021] - Asynchronous tasks on model created via the AngularJS modeler are handled as synchronous ones [ACT-4022] - TimerStartEvent with Time Cycle fires erratically [ACT-4025] - Problem import / export task listeners in Alfresco Modeler [ACT-4026] - DB2/LINUXPPC64 not supported by the Activiti workflow engine [ACT-4032] - Activiti Explorer fails to render this process when uploaded [ACT-4034] - ProcessDefinitionQuery doesn't account for tenantId [ACT-4035] - async executor with all jobs set to exclusive executes in parallel [ACT-4036] - Multi instance: loopCounter is null in the first iteration [ACT-4038] - Database product name support for DB2 for z/OS [ACT-4041] - formKey return value is null when using taskQuery with includeProcessVariables() [ACT-4042] - BulkInsert fails on DB2 (zos) Improvement [ACT-2066] - Add index on ACT_HI_TASKINST(PROC_INST_ID_) [ACT-2135] - Limit copied camel properties to activiti [ACT-2189] - Extension elements are not parsed from Lane definition [ACT-2200] - "call-activity" element click to related diagram in Eclipse activiti designer.(solved) [ACT-2233] - Unable to override DbSqlSessionFactory via spring config [ACT-4013] - Support for TerminateEndEvent in AngularJS Modeler [ACT-4014] - Handle the bulk insert queries at db level New Feature [ACT-1662] - New methods processDefinitionIds(Set<String> processDefinitionIds) and processDefinitionKeys(Set<String> processDefinitionKeys) [ACT-1832] - Provide full support for TerminateEndEvent [ACT-2008] - Support Attachments in MailActivityBehavior [ACT-2196] - Query Tasks by multiple process instance ids [ACT-2205] - Query historical processes by multiple process definition keys [ACT-4004] - Add support for conditional events [ACT-4005] - Add support for compensation boundary events, compensation intermediate throwing Events and compensation handlers [ACT-4029] - Allow tasks, historic task instances and executions to be queried by a list of process definition keys [ACT-4033] - Add execution id to Camel exchange for calling specific instance of a task Task [ACT-4000] - HistoricVariableInstanceQuery does not support filtering by executionId [ACT-4017] - Display BPMNs correctly when coordinates are negative [ACT-4028] - Issue with DrawMultilineText method Release Notes - Activiti - Version 5.17.0 Highlights We introduced a fully tested and brand new Async executor, which supersedes the old Job executor. The new Async executor uses less database queries to execute asynchronous jobs and is more performant in general. By default the Activiti Engine still uses the old Job executor, so you have to explicitly choose for the new Async executor by setting the asyncExecutorEnabled property in the process engine configuration. For more details you can look at the advanced section of the user guide. The Activiti Modeler is fully revised and is implemented using Angular JS. This is donated from the Alfresco Activiti BPMN editor that's part of the Alfresco Activiti Enterprise offering. The new Angular JS Activiti Modeler is LGPL licensed. Note that some dialogs are not yet ported to the new Activiti Modeler, but you'll also find quite a bit of new functionality, including Mule and Camel tasks and being able to define a sequence flow order for an exclusive gateway. Any help with adding new features is really appreciated. The user guide has been revamped with a rewrite to AsciiDoc It's now possible to start a process instance a skip tasks based on a skipExpression. This is very handy when you are migrating process instances to the Activiti Engine (thanks to Robert Hafner for the pull request). We added a new module named activiti-jmx to the Activiti project, which enables JMX for the Activiti Engine. Read Saeids blog for more details (thanks Saeid). Variable fetching has been optimized for process instances and executions in general. The database upgrade scripts use a different minor version than the JAR version. With the 5.17.0 release, the database version is 5.17.0.2. This versioning enables us to release snapshots and still be able to upgrade from a snapshot release to a stable release. For a 5.17.1 release the database version will be 5.17.1.x where x can be any number. So only the last minor version number can differ from the Activiti Engine release version. Specific notes Note that the version number is 5.17.0 instead of 5.17. Make sure you use version 5.17.0 for your Maven dependency. Bug fixes and various smaller improvements Check out the Release notes for more details Bug [ACT-1527] - ProcessDiagramGenerator misses instructions for message start event [ACT-1544] - Asynchronous Continuations join exception [ACT-1849] - Null pointer exception when deploying a workflow created by Activiti explorer integrated workflow editor [ACT-1900] - RuntimeService.getVariables(String, Collection) fetches all variables from DB [ACT-2061] - Suspicious instanceOf test [ACT-2062] - FeatureDescriptor's setValue method will not accept a null value [ACT-2068] - update for redundant namespace usage [ACT-2071] - Non executable processes are no validation failure [ACT-2083] - Autolayout fails with subprocess data objects [ACT-2084] - TaskService.unclaim(taskId) does not fire an ActivitiEvent [ACT-2088] - drawEventBasedGateway does not work [ACT-2093] - the label of sequenceFlow is wrong [ACT-2096] - Deadlock while concurrently finishing process instances on MSSQL [ACT-2102] - Duplicate condition in 'if' statement [ACT-2103] - Sql bug with task query when using OR statement [ACT-2116] - Fix unreachable code in org.activiti.engine.test.api.event.DatabaseEventLoggerTest [ACT-2120] - JobRetryCmd doesn't use tenantId to query the activity [ACT-2145] - Activiti Modeler UserTask form property expression problem [ACT-2151] - Deployment fails when Intermediate Event is waiting for same message as start message [ACT-2155] - Boolean field form is converted to String [ACT-2157] - Activiti diagram drawing missing some nodes [ACT-2159] - Infinite loop when using multi instance service task [ACT-2164] - The converter BPMN to XML does not manage correctly /definitions/message/@itemRef [ACT-2166] - Missing ${prefix} before ACT_ID_MEMBERSHIP in script /org/activiti/db/mapping/entity/Task.xml [ACT-2168] - Standard XML preamble is missing on SOAP request sent [ACT-2171] - NPE, integrity constraint violation, and timers firing after task completion on processes with certain combinations of timers, messages, and user tasks Improvement [ACT-2089] - Task sorting on due date needs to place those with due date null after those with a duedate [ACT-2091] - Add likeIgnoreCase for most used properties with like query [ACT-2092] - Create parent interface for TaskQuery and HistoricTaskInstanceQuery that groups all common methods [ACT-2094] - New method "getVariableInstances()" on VariableScopeImpl New Feature [ACT-1963] - Data Object support for Activiti Modeler [ACT-2073] - Document custom identity link feature in User Guide [ACT-2074] - add support for custom identity links to Modeler Release Notes - Activiti - Version 5.16.4 Highlights First implementation of a new lock free job executor (New job executor wiki). Note that exclusive jobs and suspended process instances are not supported yet. Restlet has been replaced with Spring MVC for the REST API Implementation changes https://activiti.atlassian.net/browse/ACT-2071: A deployment must always contain at least one executable (property 'isExecutable' on the process element) process definition to be deployable. However, a deployment can now contain process definitions that are not executable. Before, any non-executable process definition would result in a validation error. Now, these are warnings which do not prevent the deployment. Release Notes - Activiti - Version 5.16.1 Highlights Bug fix for database constraint issue, introduced in 5.16 Basic OR query support for process instances and task. You can now query with one OR statement chain included using the query api Release Notes - Activiti - Version 5.16 Highlights Added Spring boot support and more Spring annotation support in general (thanks to Josh Long) Refactoring of the job executor to simplify its logic and prevent possible long wait times. Added new event log table that stores process engine events when wanted, by default this is switched off. Introduction of Crystalball, which is an experimental new project that allows you to replay and simulate process instances (Thanks to Martin) Upgraded to Spring 4.x and Restlet 2.2.x Various fixes and improvements Bug fixes and various smaller improvements Check out the Release notes for more details Bug [ACT-1945] - Empty diagram produces xml parse error [ACT-1955] - Can not use table-driven editor (Kickstart) with LDAP-User [ACT-1977] - 5.15.1 release missing activiti-process-validation jar file [ACT-1978] - Obsolete signal subscriptions are not being removed, causing signals to be fired in old process definitions [ACT-1988] - Camel routes containing activiti consumers cannot be adviced. [ACT-1995] - ExtensionElement does not parse correctly and xml generation contains redundant namespace declarations [ACT-2000] - custom attribute parser fails with empty string for namespace [ACT-2005] - NPE ValuedDataObjectXMLConverter class for empty-valued data objects [ACT-2016] - Tenancy does not work for call activity [ACT-2023] - Optimize namespace usage - redundant namespace usage in XML [ACT-2026] - Modeler workspace never opens [ACT-2027] - MessageEventDefinitionParseHandler does not set extension elements. [ACT-2033] - Camel component cannot be multi instance [ACT-2042] - runtimeService.deleteProcessInstance does not set process instance end time when current activity is a scope [ACT-2047] - Incorrect self comparison of value with itself [ACT-2048] - Redundant null check in org.activiti.engine.impl.persistence.entity.JobEntity [ACT-2049] - Multi tenancy support is not working in case of duplicate filtering while deploying processes [ACT-2050] - Suspicious equals() test in org.activiti.engine.impl.persistence.entity.DeploymentEntityManager [ACT-2052] - Subprocess DataObjects created in wrong scope. [ACT-2055] - Subprocess extensions are not getting parsed [ACT-2056] - Incorrect 'contains' comparison [ACT-2057] - Equals method for ValuedDataObject should use .equals() [ACT-2058] - Null deference possible [ACT-2059] - Redundant null test in execute method [ACT-2060] - Method uses the same code for two branches Improvement [ACT-1987] - Portuguese brazilian translation of Activiti [ACT-2002] - Upgrade Restlet & Jackson libraries [ACT-2037] - Performance - Bulk deletion of variables at process end [ACT-2051] - Code change: a null check is not needed before using instanceof. New Feature [ACT-1996] - DiagramGenerator does not give possibility to change icons for custom service tasks [ACT-2011] - Expose formKey on Task [ACT-2038] - Custom IdentityLinkType support Wish [ACT-2013] - support candidateGroupIn for runtime tasks in activiti-rest Release Notes - Activiti - Version 5.15.1 Highlights Bug fix release for a MySQL upgrade issue (more details here) Some small improvements from pull requests Release Notes - Activiti - Version 5.15 Highlights Multi tanancy support added to Activiti, including the Java and REST API. Added new event support to listen for events in the Activiti Engine, like task deleted, variable updated, process engine created and many more. Introduction of data object support (thanks to Lori Small and team) Improved Spring support with great and easy to use annotations (thanks to Josh Long) Added an easy way to do custom sql execution (http://www.jorambarrez.be/blog/2014/01/17/execute-custom-sql-in-activiti/) Improved OSGi support + added OSGi unit testing using Tinybundles Various fixes and improvements Bug fixes and various smaller improvements Check out the Release notes for more details Sub-task [ACT-1608] - Fix DB2 metadata problem Bug [ACT-1339] - MultipleInstance UserTask does not use AtomicOperation -> CDI Event Listener fails [ACT-1549] - endTime of joining parallel gateway is not set: HistoricActivityInstance.getEndTime() returns null [ACT-1589] - NPE when executing SignalThrowingEvent [ACT-1610] - DbSqlSession isTablePresent method adds table prefix which causes check to fail [ACT-1627] - Save task will throw assignment-event even if assignee is not altered [ACT-1712] - Duplicate task when signal is sent to User Task [ACT-1733] - REST API documentation for Task Query points to wrong URL [ACT-1745] - ProcessDiagramGenerator misses some diagram flow elements and truncates labels [ACT-1752] - In BpmnDeployer, only schedule start timers AFTER process definition has been persisted [ACT-1794] - LDAP - Group lookups for a user fail if the DN has special characters [ACT-1795] - lineChart report hasn't been rendered [ACT-1816] - ManagementService doesn't seem to give actual table Name for EventSubscriptionEntity.class [ACT-1822] - MultiInstance loopIndexVariable support [ACT-1823] - CancelEndEvent goes into dead lock [ACT-1825] - Infinite recursion in TestActivityBehaviorFactory [ACT-1826] - OSGI bundle activiti-engine/5.14 failed to deploy due to duplicated imported org.activiti.osgi + WorkAround [ACT-1828] - Completing a task in DelegationState.PENDING does not throw ActivitiException [ACT-1838] - Activiti 5.14 did not ship incremental upgrade DB schema migration script (to auto upgrade 5.13 -> 5.14) [ACT-1839] - ACT_FK_VAR_BYTEARRAY violated on variable update [ACT-1842] - Add taskService.complete() method that takes an option boolean to determine the scope of variables [ACT-1844] - ActivitiRule fails if test methods are declared in a super class [ACT-1848] - ClassCastException when using CdiEventSupportBpmnParseHandler with multi instance user task [ACT-1854] - ExtensionElements parsing causes NullpointerException in non Process/Activity Context [ACT-1858] - Groovy generated classes aren't garbage collected [ACT-1859] - TaskListeners configured for ALL event types do not receive DELETE events [ACT-1863] - NPE on HistoricVariableInstanceQuery [ACT-1872] - Text Annotation is not generated in export and in viewer [ACT-1879] - Job Executor job acquisition can lead to deadlocks in clustered setup [ACT-1881] - activiti-engine OSGi bundle requires junit classes [ACT-1882] - activiti-spring OSGi bundle requires spring-test classes. [ACT-1883] - Listener end event is not notified when compensation done [ACT-1884] - add missing type of serviceTask in activiti-bpmn-extensions-5.15.xsd [ACT-1887] - Inserting a variable with the same name on the same process-instance from 2 threads results in duplicate name/revision entry in ACT_RU_VARIABLE [ACT-1888] - UserTask XML converter error when it has default sequence flow [ACT-1889] - Boundary event don't follow pool position. [ACT-1891] - After edit or click form properties typed enum lose values [ACT-1892] - Repeat add listener [ACT-1894] - the parameter name of HistoricActivityInstanceQuery.activityInstanceId() should be activityInstanceId, not processInstanceId [ACT-1896] - Using activityId(..) and processInstanceBusinessKey(.., true) on Execution-query causes SQL-exception [ACT-1897] - Form-properties (and some other table-based properties) no longer selectable/editable/removable in Designer 5.14.0 [ACT-1901] - The coordinates of activity nodes contained in a DiagramLayout are sometime not correct. [ACT-1908] - Activiti designer 5.14 removes custom form property information [ACT-1909] - REST queries should support paging like their normal GET counterparts do [ACT-1912] - Task Listener Bug [ACT-1914] - Activiti designer does not update sequence flow references [ACT-1918] - deploying a process fails with hibernate 4.2.6.Final "The object is already closed" [ACT-1923] - Setting task assignee and using updateTask() does not update task history [ACT-1927] - Manual tasks are actually created as generic tasks in the BPMN source [ACT-1928] - Changing the id of elements in the Properties view does not change references to it leading to broken models [ACT-1929] - Co-ordinates of BPMNEdge labels are relative to the wrong origin [ACT-1930] - Activiti designer 5.14 form editor prevents editing of fields [ACT-1935] - BeanELResolver hides target exception in ELException when catching InvocationTargetException [ACT-1937] - StackOverflowError when using an EndErrorEvent from an Event Sub-Process [ACT-1939] - HistoryService loads invalid task local variables for completed task [ACT-1940] - Possible bug in MS-SQL server configuration with schema [ACT-1941] - IdentityService interface leaking implementation details [ACT-1944] - LDAP-Authentication fails if LDAP-User has no forename [ACT-1949] - Jobs are not being removed from ACT_RU_JOB for for <timeCycle> timers [ACT-1951] - ACT_RU_JOBS has orphaned rows after deleting Process with repeat timers [ACT-1953] - multi instance sub process,variable conflict? Improvement [ACT-1491] - Parsing association, drawing Annotation and connections [ACT-1496] - Replace JSON.org with GSON dependencies [ACT-1781] - Diagram improvement: curved flows, annotations, associations, label position. [ACT-1860] - Remove unique constraint on business key [ACT-1867] - MySQL DATETIME and TIMESTAMP precision [ACT-1911] - Adopt MyBatis 3.2.4 [ACT-1916] - Double-check all REST-resources for explicit authenticate() call, to improve overall security [ACT-1952] - Refactor process definition validation + allow to plugin in custom validation New Feature [ACT-1448] - Support for expressions in candidate/assignee/... [ACT-1797] - Send events for major state changes of domain objects [ACT-1840] - Allow to set a 'category' on tasks [ACT-1890] - Multi tenancy support [ACT-1898] - Allow to execute custom SQL [ACT-1904] - Introduce Process Engine Configurator concept [ACT-1907] - Add ProcessInstanceHistoryLog [ACT-1910] - Introduce @EnableActiviti for Spring [ACT-1933] - Delete Identity Link from RuntimeService IdentityLink list [ACT-1936] - Query Tasks by process category [ACT-1954] - Warning Date on Task Task [ACT-1631] - Deploy artifacts to central maven repository [ACT-1833] - get BusinessCalendar from BusinessCalendarManager except new a instance [ACT-1869] - Document signal event scope Release Notes - Activiti - Version 5.14 Highlights Version 5.14 is focused on bug fixes and small improvements. Improved process and task query support Add OSGi bundle headers to all Activiti module JARs Thanks to the community for the large number of pull requests, especially Martin Grofcik, Mike Dias and Saeid Mirzaei Important Note for MSSQL and DB2 users Activiti 5.13 and 5.14 contain changes to the default indexes that are created when using MSSQL or DB2 as database. These indexes are not created automatically when upgrading Activiti, as they may conflict with custom indexes or impact the database load. The purpose if the indexes is to avoid deadlock exceptions when using Activiti under high load. As such, please review carefully following commits and apply to your database (where applicable): https://github.com/Activiti/Activiti/commit/4911bd176b78fa4c466e3fcbd3617af3bd95eba0 https://github.com/Activiti/Activiti/commit/22f6c8d0cf126320addb90f86495e89e7d656939 Bug fixes and various smaller improvements Check out the Release notes for more details Bug [ACT-821] - HistoryService.deleteHistoricProcessInstance() does not delete sub processes [ACT-1186] - ActivitiRule services not initialized when using SpringJUnit4ClassRunner together with @ContextConfiguration [ACT-1438] - Target expressions for call activities are not supported [ACT-1454] - Support for custom type of comment [ACT-1600] - Activiti Modeler target namespace not saved to BPMN XML [ACT-1609] - problem evaluating script while click "Reports->Process Instance Overview" on activiti-explorer.(DB2 datasource) [ACT-1612] - Modeler missing cancelActivity [ACT-1625] - Text-annotation elements are ignored when parsed, but the corresponding BPMN-DI is not which causes a warning [ACT-1649] - Deadlock occurred in load test with db2 [ACT-1663] - StartEvent hasn't the attribute "formKey" in the Activiti Modeler [ACT-1720] - Redundant space between table prefix and table name in org\activiti\db\mapping\entity\User.xml [ACT-1723] - ORA-01747: select count(RES.*) from ACT_HI_VARINST RES WHERE RES.PROC_INST_ID_ = ? [ACT-1725] - Incorrect display subprocess in subprocess, when it open in Activiti Modeler. (Problem with coordinates of elements after conversation BPMN to JSON) [ACT-1727] - activiti-explorer process instance highlight diagram is incorrect [ACT-1728] - If the process instance was started by a callActivity, it will be not have the startEvent activity in ACT_HI_ACTINST table [ACT-1731] - Fail start process with variables [ACT-1740] - REST-status cannot be created when exception contains newlines [ACT-1741] - activiti-spring POM should not depend on slf4j-log4j12 [ACT-1743] - candidateGroup can't use expression with comma [ACT-1749] - Impossible to remove identity-links from create-listener [ACT-1755] - ACT_HI_ACTINST does not record parallel gateway end times and durations [ACT-1756] - Changing ID of an activity or event makes a copy of BPMNShape element [ACT-1758] - MySQL + BTM fails on schema create [ACT-1762] - Support multiple occurrences of extension elements and attributes with same id [ACT-1770] - Model query for latest version is broken [ACT-1771] - VariableScope#getVariableLocal should take parameter of type String rather than Object [ACT-1775] - NPE when retrieving JPA Entity process variables via REST API /runtime/tasks [ACT-1777] - Activiti sql file problem for MySQL 5.6 [ACT-1780] - User Guide: Create a User (new api) does not list password for body [ACT-1788] - Fields of Java services obtained with a delegateExpression aren't injected when handling signals. [ACT-1789] - Make HistoricTaskInstanceQuery to support "taskCandidateUser" [ACT-1791] - Modeler does not allow creation ENUM type fields in forms. [ACT-1793] - Process flow through boundary events not rendered in diagram viewer [ACT-1801] - JSON Serialization of Process with Lane leads to infinite recursion [ACT-1806] - Race condition during BPMN deployment Improvement [ACT-1076] - Draw transitions names in diagram generator [ACT-1529] - Declare activiti-webapp-explorer2 as distributable in web.xml [ACT-1630] - Extend SerializableType to *not* support entities with null id [ACT-1634] - FormKey and TargetNameSpace attributes using Activiti Modeler [ACT-1665] - Add support for UUIDs as queryable process variables [ACT-1699] - Mail server configuration using JNDI [ACT-1722] - Property isExecutable aren't available in Activiti Modeler [ACT-1732] - Updating Process Business Key [ACT-1738] - Adding MDC (Mapped Diagnostic Context) logging [ACT-1747] - Upgrade some legacy dependencies (commons-lang, jackson) [ACT-1753] - Add missing sql mapping for 'selectProcessDefinition' [ACT-1782] - Provide hook to create custom UserTaskActivityBehavior [ACT-1786] - Shared packages between activiti-common-rest & activiti-rest modules [ACT-1798] - Provide getter for field MultiInstanceActivityBehavior#innerActivityBehavior [ACT-1807] - Add support for Async Message/Signal triggering from RuntimeService [ACT-1815] - Add getJobExecutor/setJobExecutor to ProcessEngineConfiguration interface New Feature [ACT-830] - Provide API ProcessInstanceQuery#processDefinitionName [ACT-1746] - Query for failed process instances [ACT-1751] - Catch Mail sending fail exception Release Notes - Activiti - Version 5.13 Highlights Fully refactored REST API. The REST API should now offer the same functionality as the Java API. Out-of-the-box LDAP integration Include process and task variables in the result of the process, historic process, task and historic task queries Lots of fixes Bug fixes and various smaller improvements Check out the Release notes for more details Sub-task [ACT-1264] - Reintroduce ExportMarshaller invocation Bug [ACT-1388] - NPE in ExecutionEntity#ensureExecutionsInitialized() [ACT-1426] - Nested multi-instance activities do not work [ACT-1542] - CommandContext Logs and Re-throws Exceptions [ACT-1578] - Update documentation: sort parameter in REST-API method /process-definitions [ACT-1591] - Referential integrity constraint violation on PROC_INST and IDENTITY_LINK [ACT-1605] - The execution of many processes at once will cause constraint violations with the ID_ field. [ACT-1607] - org.activiti.engine.ActivitiIllegalArgumentException: taskId is null when using taskService.addComment with process instance [ACT-1619] - NullPointerException when no script is given to the script task,leading to process failure [ACT-1620] - My Instances view fails to generate process diagram [ACT-1623] - NPE when eventbased gateway is after referenced event [ACT-1626] - Make not storing variable automatically in scripts the default [ACT-1641] - Process diagram not shown for certain hostnames [ACT-1653] - Deleting candidate users/groups for any user task [ACT-1654] - Camel integration does not handle exceptions propagated back by Camel DefaultErrorHandler [ACT-1655] - Wrong execution order [ACT-1656] - Designer puts Task listener class where Execution listener class should be [ACT-1657] - Wrong execution order of execution-listeners on event-based gateway and service-task [ACT-1664] - Parsing results in exception when using textannotation on collaboration tag [ACT-1668] - Auto refresh of task counts after task completion in Explorer [ACT-1669] - Possible NPE in ActivitiDiagramMatchingStrategy [ACT-1673] - CamelBehavior throws Exception when using with StrongUuidGenerator [ACT-1674] - Classloading bug when using ExpressionFactory in OSGI environment [ACT-1679] - Activiti 5.12.1 not compatible with Mybatis 3.2.2 [ACT-1685] - In activiti-web-explorer2, when call method "drawCollapsedCallActivity" throw an error "Object#<Object> has no method 'drawCollapsedTask'" [ACT-1688] - Add identity-links to history and allow querying history based on involvedUser [ACT-1690] - NPE when converting to BPMN a model with boundary event that is not attached to a task [ACT-1698] - Activiti fails to build when default charset is Windows-1252 [ACT-1702] - Setting the databaseTablePrefix not possible directly on the ProcessEngineConfiguration [ACT-1703] - The "result variable name" attribute of a Java service task is lost when exporting a process [ACT-1709] - Some fields cannot be set directly on the ProcessEngineConfiguration Improvement [ACT-1523] - Always set ProcessDefinitionId available on JobEntity [ACT-1576] - Rest API improvements for getting tasks [ACT-1606] - Create a convenience method in TaskService to set a task's due date [ACT-1616] - Expose activityID in interface Execution (add a public getter for activityID) [ACT-1642] - Deprecated IMAGE datatype under MS SQL Server [ACT-1672] - Add getKey() to PvmProcessDefinition [ACT-1704] - Option to exclude subprocesses in ProcessInstancesQuery and HistoricProcessInstanceQuery [ACT-1705] - Some methods on the {{ProcessEngineConfiguration}} are not supporting the builder pattern New Feature [ACT-1588] - Native query support paging [ACT-1647] - Enable querying TaskService for Tasks including local variables [ACT-1652] - Add support for message start events to the REST-API [ACT-1680] - Support for custom ProcessEngine selection in REST [ACT-1691] - Allow to update process definition category through RepositoryService [ACT-1696] - Provide out of the box LDAP integration [ACT-1706] - Add fullNameLike to UserQuery Task [ACT-1396] - Bring REST api in sync with Java API [ACT-1707] - Remove UserCache from Activiti Explorer Release Notes - Activiti - Version 5.12.1 Highlights Fix for ACT-1591, with referential constraint error (JIRA link) Resolved javascript JDK 7 issue (link to blog post) Small Explorer and Modeler bug fixes Other small fixes Important upgrade notes when using script tasks and auto storage of variables Due to recent changes to the JDK (removing Serializable from sun.org.mozilla.javascript.internal.Undefined), the auto storing of variables is not possible anymore with the built-in Javascript scripting engine. See this link for more information. Each script variable that needs to be stored as process variable needs to be explicitly stored by callingexecution.setVariable("variableName", variableValue). For backwards compatibility reasons, the old behavior can still be used by setting the autoStoreVariables property on a scriptTask to true. As written in the linked article from above, this might not work when using a recent JDK version and the built-in Javascript scripting engine. Release Notes - Activiti - Version 5.12 Highlights Added new javascript-based diagram viewer to Explorer (thanks to Dmitry Farafonov) Unified the BPMN parser and model for the Engine, Modeler and Designer Added Activiti Kickstart to the Explorer to quickly create processes without deep BPMN knowledge Simplified and extended the Activiti Camel module (thanks to Ryan Johnston) Added first stage of reporting functionality to the Explorer via "reporting processes" Added auto-layout module for Kickstart and BPMN import in Modeler (TBD) and Designer Added script task and execution listeners Lots of bug fixes Important upgrade note The internal org.activiti.engine.impl.bpmn.parser.BpmnParseListener interface and related classes have been removed. Yes, we know many people relied on its capabilities, although it was a class in an internal package. But do not worry: we have introduced a replacement in the api package that offers similar (and even more powerful) functionality. The code is not backwards compatible, but the required changes should be minimal (eg the XML element is replaced by a Java pojo representation of the element). Read more about it in the new 'Advanced' chapter of the userguide. Bug fixes and various smaller improvements Check out the Release notes for more details Sub-task [ACT-1565] - activiti-bpmn-converter BaseBpmnXMLConverter ignores defaultFlow for Gateways Bug [ACT-863] - HistoricVariableUpdate#getValue() throws NPE for JPA entities [ACT-995] - Not possible to update/overwrite a JPA entity in workflow [ACT-1025] - Methods deleteDeployment(String deploymentId, ...) and deleteDeploymentCascade(String deploymentId, ...) do not throw an exception with passed a non-existent deployementId [ACT-1026] - Inclusive gateway isn't properly joining sequence flows coming from call activities [ACT-1040] - TaskManager.findTaskById does not utilize session cache [ACT-1054] - Unable to complete user task coming from parallel gateway [ACT-1196] - Invalid login screen localization on non-utf-8 based hosts [ACT-1204] - InclusiveGateway with two subprocesses does not join correctly [ACT-1317] - Wrong task event generated when setting assignee to null [ACT-1345] - method name orderByHistoricActivityInstanceStartTime in class HistoricTaskInstanceQuery should be orderByHistoricTaskInstanceStartTime [ACT-1372] - User Task with form: form will be not displayed if the value expression in next line used [ACT-1377] - removeVariables() in VariableScopeImpl does not consider parent scopes [ACT-1414] - ClassCastException when completing a referenced sub process after Boundary Event with cancelActivity = false [ACT-1417] - Fix Explorer session serialization [ACT-1418] - NullPointerException if throw a not catched exception/error [ACT-1470] - Import definition type fails with CxfWSDLImporter when using complex data types [ACT-1477] - In user guide error api for form property from history detail [ACT-1479] - activiti-engine has invalid symbolic name [ACT-1486] - The ability to add an attachment to a process instance is broken [ACT-1494] - Get garbled string when render form in chines [ACT-1499] - Form properties not initialized when null or empty [ACT-1502] - Custom font name in the engine for diagram [ACT-1504] - Cannot create new user and membership in JavaDelegate using identityService [ACT-1512] - HistoricVariableUpdates no longer returned when using postgres [ACT-1516] - BPMN Converter: Adding a listener to a script task produces erroneous XML content. [ACT-1524] - Variable update detection does not work for byte[] [ACT-1528] - All variables are deleted after delete a history processinstance [ACT-1533] - Undeployment of old process versions deletes jobs for TimerStartEvents [ACT-1540] - HistoricVariableInstance does not expose taskId, nor does HistoricVariableQuery expose querying based on taskId [ACT-1546] - Impossible to assign default flow on exclusive gateways [ACT-1553] - Refactor BpmnParse to use separate handlers for each bpmn element [ACT-1555] - ProgrammaticBeanLookup doesn't regard alternatives [ACT-1556] - BpmnDeployer creates duplicates identity links when deploying processes with initiation authorization active [ACT-1579] - Process engine can be DoS'ed when deploying unsafe XML [ACT-1586] - ExecutionQuery returns wrong results when using multi instance on a receive task [ACT-1596] - Not generated boundary event in diagram created by Raphaël Improvement [ACT-1274] - Remove SEVERE level logging for expected exception in taskservice.claim() [ACT-1324] - Add specific exceptions for common error scenarios (TaskNotFoundException if a task if not found etc) [ACT-1463] - Review rest-response codes and error-body response [ACT-1498] - IntermediateCatchEventActivitiBehaviour name does not match other ActivityBehavior's [ACT-1505] - Should throw an exception if the picture is not generated [ACT-1561] - Add the ability to register a TaskListener for all event types [ACT-1568] - Query API documentation out of sync with codebase New Feature [ACT-432] - Terminate end event [ACT-1164] - Add possibility to hook in own implementation of BusinessRuleTask [ACT-1493] - Make DeploymentCache pluggable and allow to set cache limit [ACT-1511] - Extract ActivityBehavior and Task/ExecutionListener instantiation in a pluggable factory [ACT-1518] - Add generic simple workflow creator API to activiti [ACT-1537] - Allow to configure whether script variables are stored as process variables [ACT-1569] - Introduce process instance scope for signal events [ACT-1571] - Auto layout for BPMN 2.0 processes [ACT-1572] - Designer should generate XML with delegationExpression [ACT-1574] - Loop type for subprocess in Modeler [ACT-1580] - Add method to retrieve BpmnModel (Pojo version of BPMN 2.0 process) to RepositoryService [ACT-1588] - Native query support paging [ACT-1593] - Basic reporting for Explorer Task [ACT-1031] - Rename JobQuery methods [ACT-1397] - Remove Account related service operations from IdentityService [ACT-1465] - Document services in userguide [ACT-1538] - Move BpmnParseListener to public API package [ACT-1581] - Refactor ProcessDiagramGenerator to take BpmnModel as input instead of ProcessDefinitionEntity Wish [ACT-1362] - Extend JPAEntityMappings to support UUID as Id Release Notes - Activiti - Version 5.11 Highlights Added Activiti Modeler to the Activiti Explorer web application Removed buggy demo script and replaced with simpler distribution Support for doing queries that are not out-of-the-box possible with the query api (called 'native queries') Enhanced ability to query for history variable values Improved functionality to suspend and activate process definitions and instances Improved Activiti QA and added DB2 and MSSQL servers Added support for using a service call in aJava delegates (see here for more details) Lots of bug fixes Important upgrade note In 5.11 we introduce a new history table for process variables. There is an optional migration database script that you can run to generate historic variables for existing process instances. This is optional because the engine will just create those records upon update of the variable if they don't exist. The files can be found in the database directory of the Activiti distribution org/activiti/db/upgrade/optional. API Change The functionality of the repositoryService.suspendProcessDefinitionByXXX has been slightly altered. Previously, calling this method would stop the execution of jobs related to this process definition. Now, calling this method will not do this, but rather make sure a process instance cannot be created from this process definition. We've added a new method with an additional parameter 'suspendProcessInstances' which can be used to get the same behavior: when set to 'true', all the associated process instances will be suspended. This will avoid process instance continuation, including the execution of jobs related to the process instance. Bug fixes and various smaller improvements Check out the Release notes for more details Sub-task [ACT-1406] - Extend Activiti BPMN XML Schema [ACT-1407] - Marshall ID to BPMN when saving [ACT-1408] - Read and use ID when parsing BPMN model Bug [ACT-234] - CompetingJobAcquisitionTest does not work on windows [ACT-454] - Variables not being escaped when mail is sent as HTML [ACT-700] - Bug in DB2 supporting. [ACT-734] - Schema creation in mssql 2005 [ACT-785] - User Task Assignments are not handled correctly if the value of an expression holds more than one group-id or user-id [ACT-820] - Serializable values altered in scriptTask aren't persisted to DB [ACT-847] - activiti:field not injected for tasklistener on event="create" [ACT-865] - timeCycle requires seconds to be in the startDate [ACT-866] - couldn't deduct database type from database product name 'DB2/LINUXX8664' [ACT-870] - Multiple documentation elements not supported [ACT-876] - Activiti Designer creates NPE when dragging tab to second window [ACT-920] - ActivityException thrown on ProcessEngineConfiguration.buildProcessEngine() [ACT-927] - nullpointer on empty sourceref in datainputassociation [ACT-934] - Inconsistent procvar behaviour [ACT-940] - When starting the demo, support Windows 7 for "explorer.browser.open" [ACT-1002] - The Database Specific Statement is not applied in some cases [ACT-1057] - Custom tasks in Designer with only BOOLEAN_CHOICE do not write proper XML [ACT-1063] - Typo in org.activiti.engine.impl.jobexecutor.TimerDeclarationType#caledarName [ACT-1070] - activiti explorer shows mistakenly non-activiti tables if their name starts with 'ACT*' [ACT-1083] - Variables stored inside a subprocess are not linked to activity-id's. [ACT-1090] - There is not async attribute in activiti XML schema [ACT-1102] - Beans specified in activiti.cfg.xml not available in expressions [ACT-1130] - Removing identity link does not remove owner in task entity. [ACT-1147] - Posting a comment to a task causes an exception (Bad value for type long) (After updating from 5.8 to 5.9) [ACT-1174] - historyLevel property not found when creating tables manually [ACT-1180] - Add version fixing after running activiti.postgres.upgradestep.58.to.59.engine.sql to avoid the application try to autoupdate schema at restart [ACT-1187] - Default image for CustomServiceTasks is not loaded correctly [ACT-1189] - displayHelpLong of a custom property is not displayed [ACT-1239] - Exclusive gateway: Condition on default flow is not ignored [ACT-1251] - Manual upgrade throwing ActivitiWrongDbException [ACT-1252] - Signal Events and None intermediate event not rendered during Diagram Generation [ACT-1259] - Finishing last task (or signal last receive-task) of process doesn't do optimistic locking [ACT-1295] - Error BoundaryEvent in multiInstance call activity does not work as intended [ACT-1304] - EnumFormType always returns null when converting model value to form value [ACT-1316] - org.activiti.spring.SpringTransactionContext#addTransactionListener, TransactionState.ROLLED_BACK registers listener that is also invoked for successful commit [ACT-1320] - Start Timer is executed for suspended process definitions [ACT-1321] - Add NOT NULL constraint to PROCDEF table [ACT-1328] - Typo : "evalutaing" in JuelExpression [ACT-1329] - Add NOT NULL constraint on VERSION_ in PROCDEF table [ACT-1331] - RuntimeService.getVariables(String, Collection) retrieves more than the specified variables [ACT-1338] - SetProcessDefinitionVersionCmd should update TaskEntity.processDefinitionId as well [ACT-1340] - BusinessProcess.isAssociated throws NPE if no execution is associated [ACT-1341] - Activiti engine drop statement for MSSQL misses a foreign key constraint [ACT-1342] - Suspending a process with active timer can cause AcquireJobsRunnable to get into a loop that causes heavy load on database [ACT-1353] - do not generates eventBasedGateway with ProcessDiagramGenerator.generateDiagram [ACT-1361] - Broken Build: Activiti webapp REST uses junit but does not declare dependency using maven [ACT-1370] - DB2 upgrade failed from 5.9 to 5.10 because missing NOT NULL [ACT-1375] - Remove workspace with examples from distribution (maybe replace with website) [ACT-1380] - Engine should throw exception when flow out of XOR-Gateway neither has condition nor is default flow [ACT-1381] - Add support for SQL pagination in DB2 & MSSQL [ACT-1390] - Activiti incorrectly logs SEVERE level messages for expected lock collisions [ACT-1391] - Deploying a process with illegal expression doesn't cause deployment to fail [ACT-1394] - NullPointerException in UserTaskAssignmentHandler [ACT-1402] - Interrupting boundary events don't cancel all parallel sub-process instances in case sub-process is multi-instance [ACT-1410] - Executing the TaskService.deleteAttachments(String attachmentId) does not remove the file data from ACT_GE_BYTEARRAY table [ACT-1415] - TimeCycle expression is not evaluated on timer-start event [ACT-1416] - RepositoryService startableByUser() method not identity-implementation agnostic, using ACT_ID_MEMBERSHIP [ACT-1421] - Error Boundary Event - execution not closed properly [ACT-1423] - BusinessRuleTask are not converted/exported in between Activiti Modeler and Activiti Engine [ACT-1424] - Job timestamp defaults to current date when set to null (MySQL) [ACT-1429] - Remaining flowNodeRef when deleting BPMN elements within lanes [ACT-1431] - Invalid warning with exception thrown when di information is missing [ACT-1434] - Rest call contains incorrect mimetype for resource service [ACT-1435] - Exception for Expressions with 2 params in BusinessRule ruleVariablesInput [ACT-1436] - Activiti-Rest : Binding error with form properties [ACT-1440] - Activiti Explorer : Null Pointer Exception for Users with a Picture [ACT-1441] - Eclipse Plugin : Missing a parentheses on a text label. [ACT-1442] - EventBasedGateway not drawn in diagram [ACT-1444] - Race condition in CallActivityBehavior [ACT-1449] - Boundary events in designer are not deleted properly [ACT-1455] - Add support to filter on HistoricVariables values on HistoricProcessInstanceQuery [ACT-1456] - Add support for processVariableValueEquals(value) [ACT-1459] - Add support for filtering queries based on string variable values, ignoring case [ACT-1461] - The startableByUser method on the ProcessDefinitionQuery object does not return process definitions records for "candidate starter groups" when a custom session factory has been implemented [ACT-1468] - Task query: can't query by process id with ordering by due date Improvement [ACT-716] - Add log guards to org.activiti.engine.impl.interceptor.LogInterceptor [ACT-721] - Usage of Collections.synchronizedMap might cause performance degradation or java deadlock in DbSqlSessionFactory [ACT-877] - Allowing expressions in properties of CustomServiceTask [ACT-900] - TaskService.deleteTask should allow a reason to be specified [ACT-969] - Add description of JavaDelegate implementation for CustomServiceTasks to the user guide [ACT-998] - Add a ScripTaskListener [ACT-1015] - Process formKey as an expression [ACT-1023] - random order in enum fields [ACT-1064] - Remove 'selectNextJobsToExecute_mysql' and remove duplicate criterion where clause [ACT-1067] - Make BpmnDeployer#addTimerDeclarations and BpmnDeployer#removeObsoleteTimers protected [ACT-1112] - Ease integration of Activiti Explorer / Split up explorer into a JAR and a WAR example application [ACT-1122] - databaseSchemaUpdate should not downgrade, it leads to inconsistent databases [ACT-1134] - Reduce number of compensate event subscription queries when a subprocess is completed [ACT-1138] - Optimize job acquisition (exclusive jobs / maxJobsPerAcquisition) [ACT-1162] - activiti-spring pom.xml contains dependencies that should have scope 'test' [ACT-1169] - Redundant RETRIES predicate in 'selectExclusiveJobsToExecute' [ACT-1206] - GroupManager.findGroupsByUser called twice [ACT-1224] - Remove Eclipse IDE Artifacts (.classpath,...) from SVN [ACT-1273] - In the TaskService it is possible to removing variables [ACT-1279] - Add foreign key to PROC_DEF_ID_ on ACT_RU_EXECUTION [ACT-1291] - Allow subtask filtering [ACT-1301] - Refactoring BpmnParse class [ACT-1327] - activiti-osgi add blueprint context EL resolver [ACT-1336] - Support start authorization in Designer [ACT-1350] - Can not send mail use gmail smtp with ssl [ACT-1352] - Reduce the log level when a job could not found due to cancelActiviti [ACT-1383] - Enable specialization of BusinessProcess bean by moving producer methods into separate bean [ACT-1392] - Activities rendered by DiagramGenerator should show task-names on multiple lines, if too big for single line. [ACT-1405] - CustomServiceTask ID is not stored in model [ACT-1430] - Add faster and more convenient methods for retrieving form keys [ACT-1443] - Use the standard Activiti classloading mechanism for resolving serialized classes [ACT-1445] - Improve documentation for creating Designer Extensions [ACT-1447] - in Designer, The cancelActivity should not be shown for errorEventDefinition and cancelEventDefinition [ACT-1450] - Modification on ProcessInstanceResource to insert taskDefinitionKey in results [ACT-1451] - Rest API to see startableByUser process definitions [ACT-1453] - Rest API to list groups New Feature [ACT-933] - orderByDueDate for HistoricTaskInstanceQuery [ACT-936] - Provide custom navigator for Activiti projects [ACT-1020] - Get Process Documentation [ACT-1293] - Add SQL Query extensions [ACT-1300] - Add own History table for HistoricProcessVariable [ACT-1302] - Add more information to HistoricActivityInstance [ACT-1322] - Interrupting Message Event Sub-Process [ACT-1330] - Improve History Queries [ACT-1374] - Add "processVariableValueEquals" to ExecutionQuery [ACT-1378] - Change behavior with HistoricProcessVariable [ACT-1379] - Add field injection to delegateExpression [ACT-1387] - Allow service invocation in a JavaDelegate/ActivityBehavior [ACT-1457] - Suspend/Activate a process instance [ACT-1458] - Allow to suspend/activate a process definition at a given date Task [ACT-840] - Userguide refers to JPAVariableTest, but isn't present in examples anymore [ACT-1332] - Remove servlet api jar from activiti-explorer [ACT-1333] - Fix upgrade script [ACT-1334] - ad hoc task has 2 owners [ACT-1347] - Clean up cycle references [ACT-1355] - Clean up jira issues [ACT-1369] - Fix docs on starting h2 test console [ACT-1425] - Add category to deployment Release Notes - Activiti - Version 5.10 Highlights Drastic performance improvements: See Joram's blog The Activiti performance showdown for the amazing details Tijs' book Activiti in Action published by Manning came out! Added support voor bpmn message start event Added capability for clients to validate a user's rights to start a process Added support for nested sub-processes and embedded subprocesses in designer Added support for catching intermediate and boundary message events Bug fixes and various smaller improvements. Check out the Release notes for more details Bug [ACT-674] - ClaimTaskCmd should verify userId [ACT-697] - Exception thrown when bpmndi:BPMNPlane elements does not reference a process id [ACT-714] - Multi-threaded usage of non thread safe java.util.HashMap in ClassNameUtil [ACT-733] - Potential Problem with ${empty someProcessVariable} or ${someProcessVariable != null} [ACT-832] - Infinite loop in ErrorEndEventActivitiBehavior if errorEndEvent defined in a call-activity sub process and no matching boundary event [ACT-873] - typo: TaskQuery.taskUnnassigned() [ACT-886] - Process Definition Query Using CategoryLike Is Missing 'like' Clause [ACT-955] - Integrity constraint ACT_FK_VAR_EXE violated - child record found [ACT-958] - using a jobquery with processInstanceId AND executable filter fails with an SQLException [ACT-962] - Failed Testcases because of Equal Check on Exception Messages [ACT-984] - Activiti Designer: NullPointerException when saving arbitrary XML file [ACT-1000] - [Eclipse] Diagram modifications removes data in XML file [ACT-1016] - Eclipse freezes when opening xhtml page AND Activiti Designer [ACT-1019] - REST-API, getting process instance diagram error [ACT-1039] - Broken Round Trip bpmn20.xml-.activiti in Designer [ACT-1048] - HistoricalActivityInstance missing for parallel GW [ACT-1059] - Delegation state of task's not saved in database [ACT-1071] - Process Instance Diagram has wrong content-type [ACT-1075] - getting process variables through rest does not work [ACT-1077] - Wrong URL for "Delete Deployments" in the User Guide REST API Documentation [ACT-1113] - Remove deprecated cycle tables [ACT-1115] - Parsing of process definition files with several pools and DI information (for graphical representation) fails [ACT-1116] - Can't browse database tables with activiti-explorer when using Postgresql-9.1 [ACT-1125] - Missing "import java.util.Date" in HumanTimeTest causes build to fail [ACT-1129] - BooleanFormPropertyRenderer: java.lang.IllegalArgumentException: CheckBox only accepts Boolean values [ACT-1131] - Asynchronous Servicetasks with DB2 [ACT-1132] - Regression: Process Diagram generation not possible in headless mode anymore [ACT-1137] - BusinessProcess#getExecutionId() does not participate in current command [ACT-1143] - Activiti Update from 5.8 to 5.9 (using PostgreSQL) [ACT-1144] - "isExpanded" attribute missing in DI for sub processes [ACT-1146] - Class cast while saving changes - before closing editor [ACT-1150] - NullPointerException during application startup with autodeployment set to true [ACT-1154] - Missing pictures in the user guide [ACT-1156] - Callactivity with Expression always use value of first evaluation [ACT-1160] - Parsing results in Exception when using textannotation on association [ACT-1170] - 'selectExclusiveJobsToExecute' does not work for DB2 and MSSQL [ACT-1172] - activiti.mssql.create.engine.sql does not drop table ACT_RU_JOB [ACT-1176] - Parsing Collaboration Tag fails because of DI reference [ACT-1179] - Fix binary value on activiti.postgres.upgradestep.58.to.59.engine.sql [ACT-1182] - REST: Error when trying to get process variables with null value. [ACT-1183] - TimerCatchingEvent not showing up when re-opening bpmn file [ACT-1185] - When using a multi-instance subprocess, the execution is ended after first loop [ACT-1188] - JtaProcessEngineConfiguration does not use proper JTA Synchronizations [ACT-1191] - Bug: NullPointerException in FailedJobListener [ACT-1193] - REST-webapp: unable to get process instance diagram [ACT-1200] - Location of labels of sequence flows move after reopening process definition [ACT-1202] - History: tracking of multiple end events reached [ACT-1209] - Mybatis pagination does not scale on MySQL & H2 [ACT-1216] - When receycling execution it is not activated correctly [ACT-1222] - Receycling the root execution leads to stuck process instance when using call activities [ACT-1223] - Activiti Designer creates pom.xml with dependency to Activiti 5.8 [ACT-1237] - SubProcesses within pool [ACT-1238] - SignalBoundaryEvent not displayed when inside subprocess that has another subprocess as fault handler [ACT-1242] - Designer removes "Input variables" attribute of BusinessRuleTask [ACT-1245] - Diagram resource generation should only be triggered when deployment is new [ACT-1249] - ORA-00918 when processUnfinished() and orderByHistoricTaskInstanceEndTime() called on HTIQ [ACT-1253] - JobQuery 'selectJobByQueryCriteria.withExceptions' doesn't find all jobs with failures [ACT-1257] - CDI Injection for process variables does not work correctly in call stacks including other processes [ACT-1258] - ThreadpoolExecutor injection point is depending on actual class instead of Executor Interface [ACT-1260] - REST: JSON field values have to be null instead of "null" [ACT-1277] - activiti-engine-5.9.pom file has invalid URL for repository [ACT-1278] - List<HistoricProcessInstance> is not serializable [ACT-1286] - REST: Starting a process instance does not handle StartEvent property datatypes correctly [ACT-1287] - HistoricTaskInstanceQuery orderByTaskDefinitionKey() does not work [ACT-1298] - Deploying two processes with same id breaks engine [ACT-1305] - Parsing call-activity may lead to NullPointerException [ACT-1308] - Null-Pointer Exception in ProcessInstanceResource.java [ACT-1309] - NullPointerException in SignalEventHandler Improvement [ACT-380] - Add method getCandidates() to interface DelegateTask [ACT-656] - Add support for schema prefixes for table names [ACT-897] - Make JuelFormEngine.getFormTemplateString(FormData) protected [ACT-923] - HistoricProcessInstance should hold the super processInstanceId [ACT-946] - Category in ProcessDefinition REST Response [ACT-974] - Don't register Activiti XML editor as default XML editor in Eclipse [ACT-991] - improve exception thrown by taskservice.claim() [ACT-1056] - Contention in mybatis due to dynamic query creation in ExecutionEntity.remove() [ACT-1133] - Reduce number of task / event subscription queries when execution is removed [ACT-1140] - Add RuntimeService.startProcessInstanceByMessage(String, String) Method [ACT-1145] - Selecting the Activity's name from the DelegateExecution could be a convenience method [ACT-1246] - FormService returns null instead of exception if no form is defined [ACT-1276] - SetProcessDefinitionVersionCmd should work with CallActivities as well [ACT-1280] - Add unique index to process-definitions on KEY and VERSION [ACT-1284] - Improve error message if process definition with Message Start Events gets started by "startProcessInstanceByKey" [ACT-1294] - Upgrade Spring dependency to 3.1.2.RELEASE New Feature [ACT-638] - Implement non interrupting boundaryEvent (cancelActivity="false") [ACT-740] - Mechanism of management rights on the start of process. [ACT-892] - Support nested sub-processes in designer [ACT-932] - Support embedded subprocesses in Designer [ACT-959] - Retrieve process diagram through rest interface by processDefinitionId [ACT-960] - Retrieve active task list from processInstanceId [ACT-1020] - Get Process Documentation [ACT-1028] - Support default values for form properties [ACT-1046] - Allow engine to configure a delay between retries and the amount of retries. [ACT-1117] - Add support for suffix ".bpmn" instead only ".bpmn20.xml" [ACT-1136] - Add support for catching intermediate and boundary message events [ACT-1139] - Add startProcessInstanceByMessage* methods to BusinessProcess bean [ACT-1159] - BpmnError should be handled in expression based ServiceTasks. [ACT-1164] - Add possibility to hook in own implementation of BusinessRuleTask [ACT-1167] - Start and End event should be able to have Execution Listeners as well [ACT-1181] - Group provisioning & group assignment missing in REST [ACT-1190] - Add possibility to hook in own MyBatis Queries [ACT-1213] - Add "processVariableValueNotEquals" to TaskQuery [ACT-1225] - Provide infrastructure for mock testing [ACT-1282] - Add method TaskQuery#taskDelegationState(DelegationState) [ACT-1288] - Timer due date configured by expression can take java.util.Date directly - not only String with ISO 8601 Task [ACT-1161] - Remove experimental Webservices from activiti-cxf project Wish [ACT-1197] - Spring based configuration with user specified expression manager [ACT-1247] - Could u supply a WF graph with highlighted active node Release Notes - Activiti - Version 5.9 Highlights Support for Exclusive Jobs and Plugability of the Job Executor Infrastructure Persistent event subscriptions (infrastructure) Intermediate signal throw / catch Event based gateway BPMN transaction (cancel end event & cancel boundary event) BPMN compensation (compensation catch & compensation throw) Interrupting error event subprocesses (Multiple) message start events (not implemented yet, should be a quick win) Various bug fixes Bug [ACT-583] - Processes are not found in the bar file, if they are below root [ACT-736] - SpringAutoDeployment deploymentsDiffer incorrect (results in unnecessary deployment each application restart) [ACT-834] - JavaDelegate instances should not be cached [ACT-848] - Delete Reason not saved in database [ACT-861] - Statement-Leak in Mybatis [ACT-862] - VariableScopeImpl#getVariableNames does not take parent scopes into account [ACT-907] - Timer start event triggers twice [ACT-930] - startUserId missing in REST v2 /process-instances [ACT-944] - Activiti 5.7 "Create deployment artifacts" in Eclipse creates unusable .jar file [ACT-953] - Error catching boundary event that catches ALL errors doesn't work [ACT-954] - REST webapp doesn't set the authenticated user on the Authentication object [ACT-981] - ActivitiOptimisticLockingException thrown on CallActivity with Async serviceTask and Multi-Instance construct. [ACT-983] - Only test in Activiti Explorer fails [ACT-990] - Activiti-cdi: CdiActivitiTestCase troubles Jboss AS7 classloader [ACT-1001] - Activiti-cdi: taskService.claim() does not work [ACT-1003] - Exceptions thrown on a multi instance sub process with asynchronous property [ACT-1027] - Activiti engine bundle has optional dependencies declared as mandatory in OSGi manifest [ACT-1035] - Calling interrupt() on thread that access the database closes db connection and causes ConnectionClosed exceptions (H2 and Derby) [ACT-1047] - Possible infinite loop with log flooding in JobAcquisitionThread [ACT-1050] - AtomicOperationProcessEnd does not propagate caught Exceptions [ACT-1053] - Activiti cdi: Remove CommandExecutor bean [ACT-1061] - Process Image is cut off in "My instances" instead of scrolling [ACT-1062] - Database table generation fails on Oracle if multiple activiti engine schematas are visible to the database user [ACT-1087] - Add missing "parseReceiveTask" to BpmnParseListener [ACT-1097] - Execution.takeAll() takes transitions with executions which have been deleted [ACT-1098] - RuntimeService.deleteProcessInstance loses deleteReason [ACT-1099] - Parsing results in exception when using text annotation connected with association [ACT-1114] - Timer throw exception. Improvement [ACT-249] - Need a way to skip process where isExecutable='false' [ACT-326] - Store current authenticated user Id into HistoricDetail [ACT-702] - All tests should also run with full history configuration on all DB's [ACT-831] - CallActiviti to select subprocess based upon an expression evaluated at runtime. [ACT-889] - Typo error in User guide [ACT-948] - Database in activiti.cfg.xml in REST is hardcoded to H2 [ACT-964] - Also catch and handle Errors when generating diagram [ACT-1005] - Activiti jpa integration does not merge detached entities [ACT-1012] - REST - Get form properties from startform [ACT-1036] - Check for isActive flag before going to sleep in JobAcqusitionThread [ACT-1038] - Improve JobAcqusitionThread [ACT-1045] - Finding a better way for executing task rejected by ThreadPoolExecutor. [ACT-1052] - Activiti-cdi: Upgrade Jboss Weld to resolve Issue with BeforeShutdown event in ActivitiExtension [ACT-1055] - Activiti cdi: Change ProcessEngineLookup into a java.util.ServiceLoader like SPI [ACT-1072] - Execution gets stuck after nested Sub-Process with no outgoing Sequence Flows [ACT-1079] - TimerEntity#getPersistentState() should include duedate field [ACT-1104] - Overload method to pass in process variables to RuntimeService.signal [ACT-1105] - Activiti -cdi: use signal method to pass in process variables to execution New Feature [ACT-441] - Allow historic queries for a specific date range [ACT-456] - Sending mails with Activiti with TLS (eg using Gmail as SMTP) [ACT-461] - Activity type for executing OS commands [ACT-846] - Add Scrollbars to Process image in Explorer2 [ACT-883] - Variable query support for HistoricProcessInstances [ACT-903] - RuntimeService.signal should be able to take signalName [ACT-945] - Enable creating new users through rest interface [ACT-947] - Retrieve process variables through REST interface [ACT-985] - Support activiti:priority userTask extension at run-time [ACT-989] - Implement exclusive jobs [ACT-1004] - There is no ability to sort process instances by 'start time' in Activiti-API. Good if it would be... [ACT-1021] - Trigger BPMN Error Events from Java Delegate [ACT-1028] - Support default values for form properties [ACT-1034] - Fix synchronization on isActive and isJobAdded fields in JobAcquisitionThread [ACT-1068] - Error Event Sub-Process [ACT-1074] - Support suspension of JobExecution for particular ProcessDefinitions and ProcessInstances [ACT-1086] - Add "businessKey" query capabilities to TaskQuery and ExecutionQuery [ACT-1091] - Add support for persistent event subscriptions [ACT-1092] - Add support for bpmn20 signal throw and catch in engine [ACT-1093] - Add support for bpmn20 event based gateway in engine [ACT-1094] - Add support for bpmn20 compensation in engine [ACT-1095] - Add support for bpmn20 transactions in engine [ACT-1103] - Add support for bpmn20 message start event in engine [ACT-1107] - Implement None Intermediate Throw Event Task [ACT-34] - Refactor JobExecutor threading [ACT-994] - Get contributor agreement from Thilo Release Notes - Activiti - Version 5.8 Highlights Asynchronous continuations (tech preview) Added BPMN inclusive gateway Improved Spring support CDI integration improvements Bug fixes Bug [ACT-908] - Designer .activiti file won't compile and generate a BPMN 2.0 xml file [ACT-910] - duplication of flownodes when nodes are out of order [ACT-919] - Activiti-cdi: make interceptors serializable [ACT-922] - Activiti-cdi: make sure to use the right BeanManager in ProgrammaticLookups [ACT-924] - BPMN Export fails for Sequence Flows under IBM JDK because of wrong namespace definition in SequenceFlowExport.java [ACT-937] - Designer form property editor flips ID and Name fields [ACT-939] - Mime-type and extension of uploaded attachments isn't handled correctly [ACT-943] - ReceiveTask with TimerBoundaryEvent not working as expected ! [ACT-957] - Unable to add 2 timerBoundaryEvents in sub process Improvement [ACT-288] - Variable with authenticated user for be used inside form [ACT-909] - Null-check for process name in explorer2 ui [ACT-918] - Activiti-cdi: revisit process variable handling and interceptor [ACT-926] - Add method that is called when root-element of BPMN is parsed to BPMNParseListener New Feature [ACT-126] - Asynchronous continuations [ACT-890] - Add Inclusive Gateway Support [ACT-916] - Activiti-cdi: add thread context for thread-scoped associations [ACT-917] - Activiti-cdi: add execution-based associations. Release Notes - Activiti - Version 5.7 Highlights A new Activiti Explorer application completely rewritten in Vaadin The REST services have been rewritten to Restlet Important A couple of add-on applications have spun off from the Activiti download and started life on their own. Activiti Modeler continues as a Google code project called Signavio Core Components. Activiti Cycle now lives on as Camunda Fox. An evolved version of Activiti KickStart will become part of a new Alfresco cloud case management solution. Activiti Explorer has acquired Activiti Probe for an undisclosed amount :-) The resulting Activiti Explorer has been restyled and includes more dynamic task management features. The REST services were rewritten with the Restlet framework with backwards compatibility, so the REST interfaces haven't changed. The previous versions of Activiti included a REST services web application that used the Spring Surf and Webscripts framework. To implement a new or revised REST service you had to learn these frameworks. With moving to Restlet implementing new or revised REST services has become really simple. The demo setup has been limited to H2 database only, as many people struggled with getting the demo setup to run on their databases. The demo setup is a quick way to get familiar with Activiti and its tools, but it is by no means meant for production purposes. A section 'Changing the database' has been added to the userguide and is intended for advanced users who want to run the Activiti tools on their servers and databases. Sub-task [ACT-705] - Enable opening a call activity's process if it exists in the workspace Bug [ACT-481] - Replacing MailTask with ServiceTask by reconnecting connections renders different type of connection after deletion of MailTask [ACT-629] - BPMN waypoints are not created accurately [ACT-768] - JtaTransactionInterceptor should not rollback existing JTA transactions but use setRollbackOnly instead [ACT-803] - Activiti-Designer creates activiti:field for empty field-values which causes an error on deployment of bar on activiti-probe [ACT-842] - Activiti explorer pretty-time label tooltip doesn't show time, only date [ACT-856] - Error format xml in parameters of Call-Activity [ACT-867] - Login page of activiti explorer 2 is not showing in ie [ACT-879] - Designer plugin creates multiple extensionElements Nodes in a serviceTask Node Improvement [ACT-730] - Easier way to retrieve businessKey from task listeners [ACT-833] - [PATCH] make new explorer SSO friendly [ACT-835] - Make commands serializable [ACT-841] - Designer changes timer start event to none start event [ACT-843] - Rendering variable values in process-instance view should be made pluggable [ACT-860] - Ability to set targetNamespace /process definition category [ACT-880] - Add query capability to search for historic process instances based on the parent process instance id New Feature [ACT-450] - Use a Activiti XSD for the XML editor [ACT-708] - Allow dragging a shape and connector directly from the context buttons [ACT-846] - Add Scrollbars to Process image in Explorer2 [ACT-852] - Support timer start event Task [ACT-436] - Create retry interceptor [ACT-773] - Define strategy for classloading [ACT-787] - Enable probe functionality subset in the new webapp [ACT-836] - Cleaning obsolete modules from codebase [ACT-839] - Remove impl from the public javadocs [ACT-849] - Integrate explorer 2 in demo setup [ACT-857] - Limit demo setup to H2 [ACT-864] - Add support for MS SQL bootstrap Release Notes - Activiti - Version 5.6 Highlights Added direct Mule and Camel integration Easier way to retrieve businessKey from task listeners Improved support for Alfresco processes Added support for delegateExpressions in tasklistener Added support for BPMN multi instance in the eclipse designer Extended length of all user defined text columns to 4000 Known upgrade limitation In the DB schema creation scripts, we've enlarged the max size of certain varchar columns from 255 to 4000. See ACT-236. These schema updates are not part of the automatic upgrade procedure. Max length of following columns has been set to 4000: ACT_RU_JOB.EXCEPTION_MSG_ ACT_RU_JOB.HANDLER_CFG_ ACT_RE_PROCDEF.RESOURCE_NAME_ ACT_RE_PROCDEF.DGRM_RESOURCE_NAME_ ACT_RU_TASK.DESCRIPTION_ ACT_RU_VARIABLE.TEXT_ ACT_RU_VARIABLE.TEXT2_ ACT_HI_TASKINST.DESCRIPTION_ ACT_HI_TASKINST.DELETE_REASON_ ACT_HI_DETAIL.TEXT_ ACT_HI_DETAIL.TEXT2_ ACT_HI_COMMENT.MESSAGE_ ACT_HI_ATTACHMENT.DESCRIPTION_ ACT_HI_ATTACHMENT.URL_ If you want to perform these changes, you can do that manually. Check your database capabilities for the easiest way on changing the type of columns from varchar(255) to varchar(4000). As a fall back, for each table affected, you can: create a new temporary table copy the contents from the original to the temporary table delete the original table recreate the original table using the new create script with the proper lengths copy the contents from the temporary table back to the new original table drop the temporary table Bug [ACT-665] - Activiti Designer 0.8.0 can not delete by keyboard [ACT-691] - Cannot see property changes when clicking on new tasks (Activiti Engine) [ACT-786] - NPE on default sequenceFlow without id [ACT-803] - Activiti-Designer creates activiti:field for empty field-values which causes an error on deployment of bar on activiti-probe [ACT-806] - Bug: multi instance service task with collection doesn't inject collection element [ACT-808] - Support for default sequence flow in Activiti Designer [ACT-809] - Deploying Webservice [ACT-813] - deleting task cascading problem for new variable instances [ACT-826] - Rules Filter in Business Rules Task Improvement [ACT-730] - Easier way to retrieve businessKey from task listeners [ACT-795] - Refactor BPMN 2.0 validation to worker list New Feature [ACT-236] - Introduce new variable type text [ACT-781] - Allow to set charset when sending email [ACT-797] - Set a userTask priority on the bpmn20.xml [ACT-799] - Implement support for specifying timer event definitions other than timeDuration [ACT-805] - Add Ant view to Activiti perspective [ACT-807] - support definition of delegateExpressions in tasklistener [ACT-811] - Support BPMN Multi-instance Task [ACT-703] - Complement palette [ACT-780] - Enhancing the create task input field [ACT-788] - Enable Alfresco user repository integration in the new webapp Release Notes - Activiti - Version 5.5 Highlights Added CDI support Added dynamic sub task capabilities Added support for event/activity streams In the eclipse process designer,added support for default value for CustomServiceTask fields Simplified persistence Performance improvements Many bug fixes Bug [ACT-477] - Creating new diagram using wizard with existing name overwrites without warning [ACT-718] - Ternary operator not working in Listener field expression [ACT-757] - calledElement missing from call activity in generated bpmn [ACT-764] - MultiInstance doesn't work for serviceTask [ACT-765] - Initial priority from task is not stored on HistoricTaskInstanceEntity [ACT-766] - Querying tasks causes HistoricTaskInstanceEntity to be loaded for each result [ACT-782] - Javadoc of org.activiti.engine.test.Deployment is inconsistent with its implementation Improvement [ACT-653] - When folders src/test/java and src/test/resources are missing my unittests are not generated [ACT-725] - Expose task query criteria for priority >= and <= in addition to = [ACT-728] - When creating a process model not in src/main/resources (e.g. test), the activiti file is created in the src/main/resources anyway [ACT-732] - Upgrade to iBatis 3.04 [ACT-735] - Task owner is not stored on the HistoricTaskInstance [ACT-742] - Use spring to wire explorer 2 application [ACT-744] - Management pages should only be visible for admins [ACT-750] - Refactor task-lists in explorer 2 [ACT-752] - Add more demo-data to explorer 2 [ACT-753] - Navigate to task URL for task where user is not involved should be forbidden New Feature [ACT-559] - Allow default value for CustomServiceTask fields [ACT-644] - Add support for dynamic subtasks [ACT-743] - Add screen to edit user profile [ACT-746] - Add people involvement in task UI [ACT-747] - Add support for dynamic tasks in explorer UI [ACT-748] - Add support for showing/adding subtasks in UI [ACT-751] - Show event-stream in new explorer [ACT-755] - Add/remove related URL to task [ACT-759] - Allow user assignable Id for process elements. [ACT-760] - Add Skype buttons Task [ACT-741] - Refactoring persistence [ACT-754] - Apply styling to vaadin-components [ACT-767] - Add support for Events [ACT-790] - Look at cycle persistence Test [ACT-745] - Incorporate user/group administration into explorer 2 Release Notes - Activiti - Version 5.4 Compatibility note After releasing 5.3, we discovered that execution listeners and task listeners and expressions were still in non-public api. Those classes were in subpackages of org.activiti.engine.impl..., which has impl in it). org.activiti.engine.impl.pvm.delegate.ExecutionListener, org.activiti.engine.impl.pvm.delegate.TaskListener and org.activiti.engine.impl.pvm.el.Expression have been deprecated. From now on, you should use org.activiti.engine.delegate.ExecutionListener, org.activiti.engine.delegate.TaskListener and org.activiti.engine.delegate.Expression. In the new publicly available API, access toExecutionListenerExecution.getEventSource() has been removed. Apart from the deprecation compiler warning, the existing code should run fine. But consider switching to the new public API interfaces (without .impl. in the package name). Highlights Added first version of BPM-Roundtrip with Activiti Cycle (see this Screencast) Started building case management features in the engine: Added dynamic comments, attachments and due dates to tasks in Activiti Engine IMAP folder scanning for new tasks Added accounts to users in Activiti Engine Provided support to specify form properties in Activiti Designer Eclipse plugin Many bug fixes Sub-task [ACT-692] - Activiti Designer 0.8.0 can not show diagram with callActivity tasks Bug [ACT-607] - Developer Friendly BPMN doesn't remove Pools/Lanes in DI [ACT-634] - Generated Jar file is invalid [ACT-666] - Activiti 5.3 demo setup does not work on JDK 5 [ACT-667] - Diagram is cut off in some cases [ACT-668] - Mail task doesn't leave activity [ACT-670] - Designer 0.8.0 generates wrong bpmnElement references for subprocess elements [ACT-681] - Db autodiscovery for DB2 does not work on all installations of DB2 [ACT-685] - Executing processes can cause StackOverflows [ACT-695] - Exception when using HistoricVariables of type ByteArray on Postgres [ACT-731] - Webservice invocation doesn't continue process execution Improvement [ACT-655] - Throw better exception when form is not found [ACT-669] - FormService.submitStartFormData should provide a way to specify businessKey for process instance New Feature [ACT-459] - Query for tasks for a specific process definition [ACT-564] - Add dueDate to tasks [ACT-661] - Parse process definition documentation [ACT-663] - Provide support to specify form properties [ACT-672] - Allow querying task based on process-instance variable [ACT-673] - Extend HistoricTaskInstanceQuery sorting: assignee, taskId [ACT-675] - Allow querying HistoricTaskInstances based on the state of historic process-instance (running/completed) [ACT-676] - Allow querying HistoricTaskInstances based on the value of the last variable-update for a certain task-variable [ACT-677] - Query HistoricTaskInstances based on the process definition key, id and name [ACT-688] - Add comments to tasks and process instances [ACT-712] - Create feature that scans a IMAP-folder and creates task for each email in it. Task [ACT-628] - Resolve publicly exposed implementation classes [ACT-648] - Fix activiti-cycle-maven-template.zip [ACT-649] - Document process instance visualization in Probe & REST [ACT-650] - Verify links to jdk, eclipse and ant in userguide [ACT-652] - Ensure docs on how to deploy from designer to engine [ACT-679] - Add attachments to tasks and to process instances [ACT-687] - Add owner property to tasks [ACT-690] - Verify activiti dependency versions against the ones used in Alfresco [ACT-694] - Add photo, skypeid and other account data to identities [ACT-696] - Prototype new Explorer UI Release Notes - Activiti - Version 5.3 Highlights Added BPMN multi instance (==foreach) support Added BPMN intermediate timer catch event Added business rule task with Drools integration Improved Spring integrations: added possibility to limit visibility of beans and also exposed spring beans in scripts Added administrator console to manage users and groups Added automatic DB type discovery Various bug fixes Bug [ACT-485] - Fields in HistoricTaskInstance aren't updated when corresponding Task fields are updated using API [ACT-568] - Deleting an gateway does not delete the associated sequence flows [ACT-575] - Name of node in diagram not updated in specific case [ACT-589] - Kickstart does not display properly on 1024x768 [ACT-592] - Nullpointer when trying to set variables on process-instance which is waiting in a Task with a timer on it, when history-level is FULL [ACT-598] - Bug in drawing of process diagrams: sequence flow out of gateways can't have conditional markers [ACT-599] - BPMN20ExportMarshaller generates wrong id for startEvent and endEvent elements of subProcesses [ACT-603] - docs for generated processdefinition id not correct [ACT-604] - Cannot add variables to standalone task when history level is 'full' [ACT-610] - ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE does not work with Oracle [ACT-611] - It's not possible to delete sequenceFlows [ACT-612] - ServiceTask: Switching the type from class to expression doesn't update BPMN2.0.xml [ACT-618] - Call of setter before null check in file BpmnParse [ACT-621] - When creating HistoricFormPropertyEntity query for HistoricActivityInstance isn't limited to the active one [ACT-625] - Task form just displays a Failure error when deployed from Eclipse Designer [ACT-627] - Subprocess Start and End event ids incorrectly generated [ACT-629] - BPMN waypoints are not created accurately [ACT-631] - Investigate potential concurrency bug [ACT-637] - Using a call activity with activiti: in extension for variable passing results in null pointer when history level set to "full" Improvement [ACT-429] - Allow to use expressions in timer duration [ACT-484] - Add task priority to HistoricTaskInstance [ACT-529] - Processimage name does not follow conventions used by explorer/API to show or get image [ACT-530] - Deployment doesn't export process image [ACT-549] - Need brief documentation of working with Eclipse Designer Sources [ACT-584] - Add ability to delete IdentityLinks using API and DelegateTask [ACT-585] - Limiting/disabling spring-beans in expressions should be possible [ACT-587] - FieldInjection should try public setter first and revert to setting private field [ACT-600] - Review and improve 10 minute tutorial [ACT-601] - Review and improve Designer section in userguide New Feature [ACT-469] - Add support for specifying a Form key for StartEvent in Designer [ACT-483] - Support for specifying a form (form key) on a StartEvent [ACT-506] - For each support (Multi instance) [ACT-527] - Implement a Business Rule Task with Drools integration [ACT-581] - CallActivity with in/out parameters: Add sourceExpression and documentation to User Guide [ACT-582] - Add deleteHistoricProcessInstance() to HistoryService [ACT-588] - Extend TaskQuery to add variableValue querying similar to the ProcessInstanceQuery [ACT-593] - Allow firing of custom events to the active node on a given execution [ACT-596] - Result DTO of KickstartService.findKickstartWorkflowById() is missing Task-ID's [ACT-606] - Open existing BPMN XML directly in the project with the Designer [ACT-641] - Implement intermediate timer catch event Task [ACT-97] - Expose spring beans in Scripts [ACT-416] - Document how to open mgr of the in memory db [ACT-422] - Make spring parsing dependency optional [ACT-442] - expose root cause for class not found exceptions [ACT-526] - Provide Money tasks example code from userguide in repository [ACT-540] - Distill db name from the datasource [ACT-571] - Upgrade KickStart to Vaadin 6.5.0 [ACT-574] - Rename service task resultVariableName to resultVariable [ACT-623] - Simplify access to process engine configuration through Context [ACT-632] - Update repo link [ACT-642] - Include MIT license file in the activiti-modeler.war distribution file [ACT-645] - Merge Administrator branch into trunk Release Notes - Activiti - Version 5.2 Highlights First version of the jBPM-Activiti migration Visualization of the current activity in Activiti Probe Added support for BPMN error event in Activiti Engine Added support for BPMN 2.0 import in Activiti Designer Improved form datatypes Automated in container testing Various bug fixes Sub-task [ACT-512] - Add Checkbox control [ACT-513] - Add static dropdown control [ACT-514] - Add radio control [ACT-515] - Add datepicker control Bug [ACT-428] - It is not possible to use function with more then 1 argument as custom assignment handler for candidateGroups [ACT-473] - ProcessEngineFactoryBean fails to register created process engine properly [ACT-474] - Activiti Designer doesn't support multiple end events [ACT-479] - Vacation Request form example code in user guide is not correct [ACT-505] - Process definition cache not in sync when redeploying process definition with same generated id [ACT-509] - Reported progress for ExportMarshallers and ProcessValidators is incorrect [ACT-533] - In Eclipse the export process to bpmn20.xml fails in some attributes of "UserTask" [ACT-557] - Field TASK_DEF_KEY_ is not populated on ACT_HI_TASK and not exposed on HistoricTaskInstance [ACT-572] - Maven "check" profile states successful build when there are test-failures Improvement [ACT-467] - Invalid small icon path throws unclear exception [ACT-492] - Incorrect icon paths result in exceptions for CustomServiceTasks [ACT-498] - Include active activity instance id on HistoricVariableUpdate when variable is updated [ACT-499] - Start- and end-event should also be include as HistoricActivityInstance when history-level is at FULL [ACT-534] - The BPMN validator should not fail validation if CustomServiceTasks are included [ACT-567] - Allow assignee of a task to be set to null New Feature [ACT-15] - Implement BPMN boundary error event [ACT-281] - BPMN default sequence flow [ACT-354] - Add data mapping capabilities in call activity [ACT-446] - Add support for execution and task listener configuration [ACT-447] - Add support for a receive task [ACT-511] - Add support for simple controls in CustomServiceTasks [ACT-543] - Capture initiator in KickStart [ACT-554] - Return deploymentId when deploying process in AdhocWorkflowService (KickStart) [ACT-555] - Rename AdhocWorkflowService to KickstartService [ACT-560] - Improve CallActivity for independant subprocess to support parameters [ACT-563] - Start JBPM Migration [ACT-569] - Visualize current activities of process instances in probe Task [ACT-108] - Create server script for continuous integration [ACT-457] - Support the documentation element [ACT-486] - User library is not present by default [ACT-494] - Refactor deployments [ACT-496] - Change action icons to links [ACT-504] - Document exception handling in service task [ACT-510] - Check export for each PropertyType to BPMN [ACT-544] - Fix oracle metadata problem Wish [ACT-188] - Source jar files are not published on maven repository [ACT-476] - document the activiti database tables Release Notes - Activiti - Version 5.1 Warning The automatic upgrade does not have enough coverage to have full confidence. For more info, see the user guide, section Database upgrade. Changes In attribute <serviceTask delegateExpression="..."> previously always performed a leave after calling the delegate that was obtained by evaluating the expression. Which means that it's always an automatic activity. Now the leave is only performed only when the delegate object implements JavaDelegate. In case the delegate object implements ActivityBehavior the leave will not be called. In that case, the ActivityBehavior is responsible for calling leave if it wants to be an automatic activity. Highlights Added Activiti KickStart making the creation of new BPMN process models as easy as 1, 2, 3 Added automatic upgrade of the DB schema from 5.0 to 5.1 Added generation of process definition diagram based on DI information Added display of process definition diagram Added historic task instances Added Comments to artifacts in Cycle Improved Cycle Plug-In Infrastructure (now using Annotations) Fixed various bugs Sub-task [ACT-386] - Split the BPMN 2.0 marshalling into marshalling and validation parts Bug [ACT-418] - Missing groovy dependency in demo setup [ACT-419] - Remove automatic leave when using delegateExpression in serviceTask [ACT-424] - BPMN Export differs between manual and automatic and causes unnecessary exceptions [ACT-430] - SequenceFlows are only deleted for Task elements [ACT-434] - SaveHandler is only invoked from key combination [ACT-443] - HistoricDetail doesn't use activitiyInstanceId Improvement [ACT-18] - Show process definition diagram [ACT-226] - Retrieve the expression from the PvmTransition New Feature [ACT-336] - Create email service task node [ACT-385] - Create extension point to validate diagrams [ACT-408] - Allow usage of lists as property of CustomServiceTask [ACT-413] - Allow parametrization of PropertyTypes [ACT-421] - Introduce task-local variables [ACT-455] - Make parselisteners configurable on ProcessEngineConfiguration [ACT-464] - Introduce HistoricTaskInstance [ACT-470] - Create and include Activiti KickStart in the distribution Task [ACT-293] - Refactor variable map [ACT-420] - Automatic upgrade [ACT-439] - Extract image generation in code in module activiti-engine [ACT-440] - Add process image generation to bpmn deployer [ACT-444] - Add task query criteria for taskDefinitionKey and -Like [ACT-458] - Check if seperate selectTaskByQueryCriteria is needed for MySQL in task.mapping.xml [ACT-471] - Add taskId ref to HistoricDetail Release Notes - Activiti - Version 5.0 Highlights Various bug fixes Activiti Engine: Added more configuration options and synced standalone with Spring configuration Activiti Engine: Added task listeners Activiti Probe: Deployment through file upload Activiti Cycle: Loads of improvements for the GUI, tagging and linking, new connectors for SVN and SFTP, Maven project generation Activiti Designer: Possibility to add your own developed nodes to the Designer with the Designer extension functionality Activiti Designer: BPMN 2.0 XML editor with content assist Activiti Designer: Deployment editor with support to generate a BAR file Activiti Designer: Support for e-mail and manual tasks Activiti Designer: Field extensions editor for the Service task and support for expressions Activiti Designer: Support for Formkey attribute for User tasks Activiti Designer: Automatic generation of the BPMN 2.0 XML and a process image after each save of the editor diagram (no need to for an explicit export). Bug [ACT-194] - Business model is not persisted when reconnecting SequenceFlow [ACT-215] - TaskEntity does not populate processInstanceId [ACT-217] - Review documentation [ACT-225] - Cannot see database view in Probe [ACT-255] - Access via url to Explorer lose url after authentication. [ACT-295] - Sorting processes on name doesn't work [ACT-297] - Error when creating link in activiti-cycle on postgres DB [ACT-306] - Nullpointer exception when sequence flow has invalid destination [ACT-308] - Review DeployBarTask handling when no ProcessEngine is found [ACT-314] - REST-call task get doesn't return valid JSON [ACT-318] - Cannot open PNG picture in Activiti-Cycle if the model name has whitespaces in the name [ACT-320] - Replace of JAVA_OPTS in demo setup not working on Windows [ACT-323] - Business key not persisted in HistoricProcessInstance [ACT-329] - HistoricProcessInstance startActivityId() and endActivityId() is always null [ACT-334] - Remove check whether user/group exists when claim/addCandidateXX/etc. [ACT-338] - Pagination Links in Processes List in Activiti Explorer doesn't work [ACT-347] - Fix Spring bean usage in ServiceTask [ACT-351] - Possible illegal query results when querying ProcessInstances by date/long variable value [ACT-352] - HistoricVariableUpdate of type byteArray/serializable throws NPE when calling getValue() [ACT-353] - Boolean variables are stored as bytearray [ACT-366] - Problems with Umlauts in Modeler [ACT-369] - Modeler requires Java 6 to run [ACT-371] - Latest SVG API doesn't work with Activiti Modeler examples [ACT-377] - Empty diagrams have no Process entity by default [ACT-379] - Methode getLabels() is missing in Shape [ACT-391] - Exception with BPMN 2.0 XML Export of examples in Modeler. [ACT-403] - Cycle in demo-setup throws exception when opening 'Activiti Modeler' node in tree [ACT-405] - Cycle create-scripts for oracle contain errors [ACT-409] - Task form properties not persisted on history level audit Improvement [ACT-162] - Simplify extensibility of identity component [ACT-212] - Prefix foreign keys with FK_ACT_* instead of FK_* [ACT-252] - Typo in warning message is misleading: "XPath currently not supported as typeLanguage" [ACT-274] - Switch to Spring Surf/Webscripts 1.0 for all web applications & rest api (and use its new abstract authenticator base class) [ACT-292] - Stop making default event being bookmarked [ACT-327] - Add finished() to the HistoricActivityInstanceQuery to be able find all completed activities [ACT-328] - add getId() into HisctoricActivitiInstance and add search by id into HistoricActivitiInstanceQuery [ACT-359] - Invoke export to BPMN 2.0 automatically via ExportMarshaller extension point New Feature [ACT-211] - BPMN: assignment handler [ACT-222] - Make activiti jars valid OSGi bundles [ACT-235] - Add an OSGi extender to deploy activiti processes as osgi bundles along with the needed URL handlers and fileinstall deployers [ACT-278] - Add support for extending designer's functionality with custom service tasks [ACT-290] - SVG API should not peform access to Signavio Academic Version in the Internet [ACT-300] - Deploy business archive with Activiti Probe [ACT-301] - Expose getFormService() for ActivitiRule [ACT-360] - Provide an osgi web bundle for the rest api [ACT-363] - Bug with relative paths in Modler Backend [ACT-364] - The BPMN 2.0 serialization isn't the latest version [ACT-365] - The BPMN 2.0 DI is still the early draft, not the spec version [ACT-367] - Release a new Maven version of the Signavio core components and upload them to the alfresco repository [ACT-396] - activiti.cfg.jar should go in webapps\activiti-rest\WEB-INF\lib as well Task [ACT-123] - Review the user guide for experimental features [ACT-163] - Investigate if smaller groovy jar is sufficient [ACT-171] - Remove model repository workaround in demo.setup [ACT-180] - Fix forced downloads in mule build [ACT-205] - Create Ant script to assemble update site [ACT-256] - Changed config param dbSchemaStrategy to databaseSchemaUpdate [ACT-296] - Fix date picker in safari [ACT-299] - Fix JobQuery test [ACT-311] - Rename JavaDelegation to JavaDelegate [ACT-315] - fix wsdl importer parsing [ACT-317] - Improve lib dependency management in distribution [ACT-321] - Add orderByCreateTime() to TaskQuery [ACT-332] - Make variable types configurable [ACT-335] - Move modeler patching from demo setup to a dedicated build [ACT-337] - Refactor distribution to remove maven and include the libs in the distro [ACT-340] - Add process definition diagram resource property [ACT-342] - Make sessions configurable [ACT-348] - Make handling of process definition resource name and other resource names consistent [ACT-349] - Delete rest 2 webapp [ACT-355] - Test java delegations in demo setup after distro refactoring [ACT-358] - Lazy initialization of delegation classes [ACT-378] - Unify and simplify configuration [ACT-381] - Verify history level configuration [ACT-383] - Fix exception message [ACT-393] - Create build file for example projects to deploy business archives and classes [ACT-394] - Review setup target cfg.create [ACT-395] - Doc URIEncoding for Activiti Modeler [ACT-399] - Check driver jars and setup demo.start with other dbs [ACT-400] - Add release notes to the readme.html [ACT-401] - Add cxf module to the checkmule profile [ACT-402] - Add version number to userguide title 5.0.rc1 (November 1, 2010) Highlights Activiti Probe added Job and Deployment management Event listeners Query for process instances based on variable values Parameterized method expressions History details and audit capabilities Extracted FormService and improved flexible form handling Activiti config file from properties to xml PostgreSQL en Oracle support Improved DB performance by fine tuning indexes Known limitations [ACT-294] - Currently the forms as worked out in Activiti Explorer do not yet use the submitStartFormData and submitTaskFormData. So the form properties are not yet archived when using the forms in Activity Explorer. Sub-tasks [ACT-191] - Documentation Mistake in ProcessEngineFactoryBean section of User Guide Bug [ACT-144] - Canot start process instance when sorted on version first [ACT-192] - Table "ACT_ID_GROUP" not found for dbSchemaStrategy = "create-drop" [ACT-194] - Business model is not persisted when reconnecting SequenceFlow [ACT-195] - UpdateConnectionFlowElementFeature is not invoked for SequenceFlows [ACT-206] - Cannot unclaim a task [ACT-233] - Connection pool of Ibatis is not used in standalone usage [ACT-287] - Starting process instance in explorer doesn't show 'Start form' anymore when the process has a startform Improvement [ACT-21] - Manage list of deployments [ACT-22] - Manage list of jobs [ACT-23] - Add no-wrap to task list menu navigation [ACT-129] - Make connection pool of MyBatis configurable [ACT-138] - Create REST API for Manage list of jobs [ACT-139] - REST API for Manage list of deployments [ACT-179] - Fix css code so its consistent with the rest of the app [ACT-189] - Engine should be able to resolve parameterised method expressions [ACT-196] - Process and subprocess diagram should have default content [ACT-197] - SequenceFlow arrowheads are mispositioned and too large [ACT-209] - Expose Task start time in interface and query API [ACT-210] - Rename ActivitiRule.getHistoricDataService() to ActivitiRule.getHistoryService() [ACT-213] - Verify all basic indices exist on supported databases [ACT-214] - Clarify documentation demo setup [ACT-216] - ServiceTaskMethodExpressionActivityBehavior / ServiceTaskValueExpressionActivityBehavior should support storing service task return value in process variable [ACT-219] - Update Userguide with latest UI changes [ACT-224] - Allow parsing of document element for all activity types [ACT-230] - Change BpmnParse: instead of throwing an exception, use the addProblem() method [ACT-238] - Extract common methods from Query API to single Interface [ACT-241] - Handle closing of inputstreams in a consistent way [ACT-246] - Missing warning when process has a construct which is unsupported. [ACT-257] - Define a order for group list (left panel) [ACT-259] - Make method naming in Query API consistent [ACT-260] - Make namespace prefix consistent activiti: [ACT-261] - Change dashes in xml-entities to camelCase [ACT-265] - Always use ReflectUtil to do classloader-related operations [ACT-272] - Only use 'Expression' instead of making distinction between value/method New Feature [ACT-120] - Audit tracking [ACT-125] - BPMN: add event listeners [ACT-145] - Add Task.getTaskDefinitionKey() [ACT-148] - Make Activiti "offline runnable" [ACT-152] - Introduce business key [ACT-190] - Query for process instances based on variable values [ACT-208] - Cannot remove a variable [ACT-220] - Allow for parameter injection in method-expr on service-task [ACT-242] - Expose process definition model for introspection [ACT-258] - Introduce form instances [ACT-273] - Allow user to customize preference of automatically adding labels to sequence flows Task [ACT-134] - Revisit configuration [ACT-137] - Add oracle support [ACT-174] - Document link to Signavio community in our wiki [ACT-183] - Remove 'about' tabs in the webapps [ACT-186] - Improve exception analysis when no tables are present [ACT-193] - Bring CYCLE_CONFIG in sync with rest of table naming conventions [ACT-243] - Expose Execution.getProcessInstanceId [ACT-245] - review variable in ByteArrayEntity [ACT-247] - Clean unused table columns [ACT-248] - Verify HistoricProcessInstance query filtering [ACT-251] - Add task audit to history [ACT-262] - Check docs about bar file classloading [ACT-263] - Use a correct versioning scheme for activiti-juel module [ACT-264] - Remove eclipse/idea files from trunk [ACT-266] - Make demo-setup run on Postgres [ACT-267] - Merge pvm and juel modules into engine [ACT-282] - Transform BpmnJavaDelegation class to JavaDelegation interface [ACT-283] - rename activiti bpmn extensions namespace Test [ACT-221] - Add an integration test for the webservice task solely based on CXF 5.0.beta2 (October 1, 2010) Highlights Added Activiti Designer, an eclipse plugin for process authoring targetted for developers Design BPMN processes grafically: start event, end event, sequence flow, parallel gateway, exclusive gateway, embedded subprocess, script task, user task and service task. Generate JUnit test case by right click on the process in the package explorer Run the JUnit test with an embedded h2 database Configure Java class for a service task Configure assignee or candidate for a user task Configure script with a script task Added DB support for MySQL and PostgreSQL Activiti Modeler and Activiti Engine are now synced on the final BPMN 2.0 specification New improved version of Activiti Modeler Loads of Activiti Cycle improvements Added JDK 5 compatibility Added history activity instances Added unit testing support Added email support and receive activity Added optimistic locking for out-of-the-box clustering support Added more query APIs Minor API cleanup Bug [ACT-1] - Change init servlet into context listener [ACT-56] - Activiti Modeler is bound to localhost instead of the actual servername [ACT-57] - Condition on sequence flow is not saved to bpmn20.xml file after reopening an existing process [ACT-71] - Activiti Modeler doesn't work if not installed by demo setup [ACT-76] - JSON Response contains unescaped control caharcters [ACT-94] - java.util.logging.ErrorManager/ NullPointerException in catalina.out at startup of tomcat [ACT-113] - Modeler "Save" does not regenerate bpmn20.xml file [ACT-114] - Unable to save newly created diagram twice [ACT-115] - Table ACT_GE_PROPERTY cannot be created on MySQL with UTF-8 encoding due to limitation of key index length [ACT-142] - Logo is linked to Signavio jBpm page [ACT-181] - Automatic deployment on resource change doesn't work Improvement [ACT-7] - Move logging.properties process activiti-engines-init to tomcat installation [ACT-23] - Add no-wrap to task list menu navigation [ACT-64] - Configure Tomcat in demo setup to have more memory [ACT-66] - Make task form rendering consistent in API [ACT-68] - Make demo.setup run on MySQL [ACT-69] - Add ant target to start up h4 console [ACT-70] - Review API and build systematic test coverage [ACT-73] - Document usage of activiti prefix [ACT-104] - Replace findXxx methods returning lists with query API [ACT-140] - REST API for Show process definition diagram [ACT-158] - ScriptTaskActivity should support storing script execution result in a process variable with configurable name [ACT-182] - Add internal support for create-if-necessary db schema stragegy New Feature [ACT-35] - Provide external URL for navigating task forms [ACT-83] - Capture the initiator [ACT-109] - BPMN: document receive task [ACT-146] - Add Activiti FavIcon to webapp [ACT-168] - Introduce identityLink in API Task [ACT-30] - Finish the basic history data model [ACT-44] - Verify exception and rollback behavior in Spring context [ACT-49] - make activiti compatible with jdk 5 [ACT-52] - Remove BPMN 2.0 Beta compatibility [ACT-67] - Rename modules activiti-probe to activiti-webapp-probe, similar for activiti-explorer [ACT-78] - move sortorder out of the interface package [ACT-84] - move parsing of value expression to bpmn parser [ACT-87] - Fill exception field when job fails [ACT-89] - Review test support [ACT-95] - fix testTwoNestedSubProcessesInParallelWithTimer [ACT-103] - Consider removing Page from engine interface [ACT-105] - Add auto redirect to Activiti Modeler [ACT-106] - Add testing for optimistic locking [ACT-110] - Move Chapter 11. Running QA tests to wiki [ACT-124] - Document library dependencies [ACT-127] - Restructure modules [ACT-130] - Reupload Maven artifacts due to checksum error [ACT-131] - Switch to new repository/build for Ativiti Modeler [ACT-133] - Refactor start process instance [ACT-135] - Cleanup unused task properties [ACT-136] - Verify MySQL [ACT-153] - Replace DbSchemaStrategy enum with String [ACT-155] - Create Junit @Rule test with ActivitiRule and document it [ACT-156] - Revise Java service task: introduce BpmnJavaDelegation and field injection [ACT-157] - Create mail activity [ACT-159] - Remove expressionLanguage and typeLanguage in examples [ACT-167] - fix excluded test RepositoryConnectorConfigurationManagerImplTest [ACT-177] - Document configuration file properties 5.0.beta1 (September 1, 2010) Known limitations Optimistic locking isn't tested yet [ACT-106] History only contains HistoricProcessInstances, no HistoricActivityInstances yet [ACT-30] Some API changes are still expected [ACT-104] New Features [ACT-55] - Introduce first version of Activiti-Cycle [ACT-91] - Expand serviceTask with method and value expressions Bugs [ACT-39] - fix ProcessEngineInitializationTest [ACT-98] - REST API errors [ACT-99] - Table records don't show Improvements [ACT-2] - Clean up API [ACT-6] - Rename DbProcessEngineBuilder to ProcessEngineBuilder [ACT-60] - pvm refactoring [ACT-79] - Please add a ELResolver that automatically resolves any bean in the Spring BeanFactory in which the ELResolver resides [ACT-80] - Extend service task to SignallableActivityBehavior [ACT-82] - Expose form attribute from Task object Tasks [ACT-48] - Add Grails plugin link to docs or website [ACT-59] - define table/column naming strategy [ACT-61] - Introduce query API for deployments and resources [ACT-75] - document maven repo in the website community page [ACT-85] - Improve error message when db driver is not found [ACT-86] - Fix classpath in setup script for other dbs [ACT-90] - Add Spring integration examples and documentation [ACT-96] - Cycle demo build creates 2 files in codebase [ACT-101] - Add sorting of the table names in Probe 5.0.alpha4 (August 1, 2010) Improvements MySQL support Support for method expressions on sequence flow Revised ActivityExecution API Added ConcurrencyController API Process Event Bus Taskforms: added date and date picker support Explorer: changed process definition drop down list to a separate page New features BPMN parallel gateway BPMN manual task BPMN (embedded) subprocess BPMN call activity (subprocess) BPMN Java service task Spring integration (experimental, no docs yet) Bugfixes Made engine compatible with BPMN 2.0 beta process models Fixed exception on windows and linux when using boundary timer event Expression cannot have whitespaces 5.0.alpha3 (July 1, 2010) Improvements Switch from iBatis to MyBatis JobExecutor BPMN Timers BPMN JSR 223 script support. Updated to a newer version of BPMN xsd Query API Switched JUnit usage from 3-style inheritance to 4-style annotations 5.0.alpha2 (June 1, 2010) Improvements Task forms in Activiti Explorer Database table content viewer in Activiti Probe Exclusive gateway Unified Expression Language support Reduced download size Known limitations No history in Activiti Engine Only single DB: H2 Only one tx demarcation tech: standalone JDBC No process concurrency 5.0.alpha1 (May 17, 2010) Known limitations No history in Activiti Engine Only single DB: H2 Only one tx demarcation tech: standalone JDBC No task forms No process concurrency No Activiti Cycle
SlidingMenu的一些常用属性 原文转载http://blog.csdn.net/zwl5670/article/details/48274109 [java] view plain copy //设置侧滑菜单的位置,可选值LEFT , RIGHT ,LEFT_RIGHT(两边都有菜单时设置) menu.setMode(SlidingMenu.LEFT_RIGHT); //设置触摸屏幕的模式,可选只MARGIN ,CONTENT menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); //根据dimension资源文件的ID来设置阴影的宽度 menu.setShadowWidthRes(R.dimen.shadow_width); //根据资源文件ID来设置滑动菜单的阴影效果 menu.setShadowDrawable(R.drawable.shadow); //这两个都是设置滑动菜单视图的宽度,二选一 //设置SlidingMenu离屏幕的偏移量 menu.setBehindOffsetRes(R.dimen.slidingmenu_offset); //设置宽度 menu.setBehindWidth() // 设置渐入渐出效果的值 menu.setFadeDegree(0.35f); //设置SlidingMenu与下方视图的移动的速度比,当为1时同时移动,取值0-1 menu.setBehindScrollScale(1.0f); //设置二级菜单的阴影效果 menu.setSecondaryShadowDrawable(R.drawable.shadow); //设置右边(二级)侧滑菜单 menu.setSecondaryMenu(R.layout.right_menu_frame); //为侧滑菜单设置布局 menu.setMenu(R.layout.leftmenu); //把滑动菜单添加进所有的Activity中,可选值SLIDING_CONTENT, SLIDING_WINDOW menu.attachToActivity(this,SlidingMenu.SLIDING_CONTENT); 1、在Activity中通过SlidingMenu构造方法,直接设置侧滑菜单 [java] view plain copy public class MainActivity extends Activity{ @Override protected void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SlidingMenu menu = new SlidingMenu(this);//初始化滑动菜单 menu.setMode(SlidingMenu.LEFT);//设置触摸屏幕的模式 menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); menu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影的宽度 menu.setShadowDrawable(R.drawable.shadow);//设置滑动菜单的阴影效果 // 设置滑动菜单视图的宽度 menu.setBehindOffsetRes(R.dimen.slidingmenu_offset); // 设置渐入渐出效果的值 menu.setFadeDegree(0.35f); /** *SLIDING_WINDOW will include the Title/ActionBar in the content *section of the SlidingMenu, while SLIDING_CONTENT does not. */ // 把滑动菜单添加进所有的Activity中 menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT); // 为侧滑菜单设置布局 menu.setMenu(R.layout.leftmenu); } } 2、通过把Activity继承SlidingActivity a、继承SlidingActivity b、然后在onCreate中setBehindContentView(R.layout.leftmenu); 设置侧滑菜单的布局 c、通过getSlidingMenu()得到SlidingMenu对象,然后设置样式 [java] view plain copy public class MainActivity extends SlidingActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setBehindContentView(R.layout.leftmenu); // configure the SlidingMenu SlidingMenu menu = getSlidingMenu(); menu.setMode(SlidingMenu.LEFT); // 设置触摸屏幕的模式 menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN); menu.setShadowWidthRes(R.dimen.shadow_width); menu.setShadowDrawable(R.drawable.shadow); // 设置滑动菜单视图的宽度 menu.setBehindOffsetRes(R.dimen.slidingmenu_offset); // 设置渐入渐出效果的值 menu.setFadeDegree(0.35f); /** * SLIDING_WINDOW will include the Title/ActionBar in the content * section of the SlidingMenu, while SLIDING_CONTENT does not. */ // menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT); // menu.setMenu(R.layout.leftmenu); } } 3、将SlidingMenu当作普通控件 可以把SlidingMenu作为普通的view,然后在布局中声明。 [java] view plain copy <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/id_main_ly" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginTop="30dp" > <com.jeremyfeinstein.slidingmenu.lib.SlidingMenu xmlns:sliding="http://schemas.android.com/apk/res-auto" android:id="@+id/slidingmenulayout" android:layout_width="120dp" android:layout_height="170dp" android:background="#ffffffff" sliding:behindOffset="0dp" sliding:behindScrollScale="1" sliding:fadeDegree="0.3" sliding:fadeEnabled="true" sliding:touchModeAbove="fullscreen" sliding:viewAbove="@layout/pic" /> </LinearLayout> </RelativeLayout> pic布局: [plain] view plain copy <?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="120dp" android:layout_height="170dp" android:src="@drawable/zhy" /> MainActivity: [java] view plain copy package com.zhy.zhy_slidemenu_demo03; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; public class MainActivity extends Activity { private SlidingMenu mLeftMenu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLeftMenu = (SlidingMenu) findViewById(R.id.slidingmenulayout); // configure the SlidingMenu // SlidingMenu menu = new SlidingMenu(this); mLeftMenu.setMode(SlidingMenu.LEFT); // 设置触摸屏幕的模式 mLeftMenu.setShadowWidthRes(R.dimen.shadow_width); mLeftMenu.setShadowDrawable(R.drawable.shadow); mLeftMenu.setMenu(R.layout.leftmenu); mLeftMenu.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mLeftMenu.isMenuShowing()) mLeftMenu.toggle(); } }); // 设置滑动菜单视图的宽度 // 设置渐入渐出效果的值 /** * SLIDING_WINDOW will include the Title/ActionBar in the content * section of the SlidingMenu, while SLIDING_CONTENT does not. */ } } 4、SlidingMenu设置左右侧滑菜单例子 上面3个方法,SlidingMenu的布局中控件的事件都需要写在Activity中,这样代码比较臃肿,一般会使用Fragment作为侧滑菜单的布局容器。 核心代码: · [java] view plain copy Fragment leftMenuFragment = new MenuLeftFragment(); · setBehindContentView(R.layout.left_menu_frame); · getSupportFragmentManager().beginTransaction() · .replace(R.id.id_left_menu_frame, leftMenuFragment).commit(); · SlidingMenu menu = getSlidingMenu(); · menu.setMode(SlidingMenu.LEFT_RIGHT); · // 设置触摸屏幕的模式 · menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); 先给侧滑菜单通过 setBehindContentView(R.layout.left_menu_frame); 设置一个布局,此布局中只有一个FrameLayout,然后使用FragmentManager将Fragment替换掉此Fragment,这样这个 Fragment就作为我们侧滑菜单的布局了,我们的事件处理代码也可以写在Fragement中,而不是Activity中。 MenuLeftFragment [java] view plain copy package com.zhy.zhy_slidemenu_demo04; import java.util.Arrays; import java.util.List; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListAdapter; import android.widget.ListView; public class MenuLeftFragment extends Fragment { private View mView; private ListView mCategories; private List<String> mDatas = Arrays .asList("聊天", "发现", "通讯录", "朋友圈", "订阅号"); private ListAdapter mAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mView == null) { initView(inflater, container); } return mView; } private void initView(LayoutInflater inflater, ViewGroup container) { mView = inflater.inflate(R.layout.left_menu, container, false); mCategories = (ListView) mView .findViewById(R.id.id_listview_categories); mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mDatas); mCategories.setAdapter(mAdapter); } } MenuRightFragment package com.zhy.zhy_slidemenu_demo04; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class MenuRightFragment extends Fragment { private View mView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(mView == null) { mView = inflater.inflate(R.layout.right_menu, container, false); } return mView ; } } MainActivity [java] view plain copy public class MainActivity extends SlidingFragmentActivity { private ViewPager mViewPager; private FragmentPagerAdapter mAdapter; private List<Fragment> mFragments = new ArrayList<Fragment>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); // 初始化SlideMenu initRightMenu(); // 初始化ViewPager initViewPager(); } private void initViewPager() { mViewPager = (ViewPager) findViewById(R.id.id_viewpager); MainTab01 tab01 = new MainTab01(); MainTab02 tab02 = new MainTab02(); MainTab03 tab03 = new MainTab03(); mFragments.add(tab01); mFragments.add(tab02); mFragments.add(tab03); /** * 初始化Adapter */ mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public int getCount() { return mFragments.size(); } @Override public Fragment getItem(int arg0) { return mFragments.get(arg0); } }; mViewPager.setAdapter(mAdapter); } private void initRightMenu() { Fragment leftMenuFragment = new MenuLeftFragment(); setBehindContentView(R.layout.left_menu_frame); getSupportFragmentManager().beginTransaction() .replace(R.id.id_left_menu_frame, leftMenuFragment).commit(); SlidingMenu menu = getSlidingMenu(); menu.setMode(SlidingMenu.LEFT_RIGHT); // 设置触摸屏幕的模式 menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); menu.setShadowWidthRes(R.dimen.shadow_width); menu.setShadowDrawable(R.drawable.shadow); // 设置滑动菜单视图的宽度 menu.setBehindOffsetRes(R.dimen.slidingmenu_offset); // 设置渐入渐出效果的值 menu.setFadeDegree(0.35f); // menu.setBehindScrollScale(1.0f); menu.setSecondaryShadowDrawable(R.drawable.shadow); //设置右边(二级)侧滑菜单 menu.setSecondaryMenu(R.layout.right_menu_frame); Fragment rightMenuFragment = new MenuRightFragment(); getSupportFragmentManager().beginTransaction() .replace(R.id.id_right_menu_frame, rightMenuFragment).commit(); } public void showLeftMenu(View view) { getSlidingMenu().showMenu(); } public void showRightMenu(View view) { getSlidingMenu().showSecondaryMenu(); } } MainActivity继承的是SlidingFragmentActivity ,在Activity中FragmentPagerAdapter和viewPager作为主布局,然后分别初始化SlidingMenu的两边的菜单。
eclipse编译maven项目的时候,保存信息如下: [INFO] Changes detected - recompiling the module! [INFO] Compiling 198 source files to F:\QunLiVideo\code20141106\jobserver\target\classes [INFO] ------------------------------------------------------------- [ERROR] COMPILATION ERROR : [INFO] ------------------------------------------------------------- [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? [INFO] 1 error [INFO] ------------------------------------------------------------- [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.281 s [INFO] Finished at: 2015-01-21T20:22:29+08:00 [INFO] Final Memory: 9M/158M [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.0:compile (default-compile) on project jobserver: Compilation failure [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK? [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException 下载java jdk,并安装java jdk。下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 在eclipse的菜单中,进入 Window > Preferences > Java > Installed JREs > Execution Environments,选择JavaSE-1.6, 在右侧选择jdk. 然后在maven菜单中使用 “update project ...”. 但是试了时候不好用 但是重复尝试了很久都不行。 需要执行下以命令: Maven update Maven clean project cleanbuild project 然后多次执行Maven Install,直到打包成功为止。
AES加密,就是对称加密。分享牛系列,分享牛专栏,分享牛。客户端可以保存一个密钥,调用加密的值传入需要加密的值,然后程序根据密钥算出一个新的值,当然了解密也需要,对应的密钥才可以解密。 写一个程序如下: /** * */ import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.HashMap; import java.util.Map; /** * @author qq 1515308 */ public class AESEncrypter { public static void main(String[] args) { String encryptAsString = new AESEncrypter().encrypt("qq:1515308"); System.out.println(encryptAsString); String decryptAsString = new AESEncrypter().decryptAsString(encryptAsString); System.out.println(decryptAsString); } private static String aesKeyStr = "NGQxNmUwMjM4M2Y0MTI2MTM3NDI0Y2MxMjA1N2IyNDM="; private SecretKey aesKey; private AESEncrypter() { aesKey = loadAesKey(); } private AESEncrypter(String aes) { aesKey = loadAesKey(aes); } private static AESEncrypter INSTANCE; private static Map<String, AESEncrypter> INSTANCES = new HashMap<>(); public static AESEncrypter getInstance() { if (INSTANCE == null) { synchronized (aesKeyStr) { if (INSTANCE == null) { INSTANCE = new AESEncrypter(); } } } return INSTANCE; } public static AESEncrypter getInstance(String aes) { if (INSTANCES.get(aes) == null) { synchronized (aesKeyStr) { if (INSTANCES.get(aes) == null) { INSTANCES.put(aes, new AESEncrypter(aes)); } } } return INSTANCES.get(aes); } public String encrypt(String msg) { try { Cipher ecipher = Cipher.getInstance("AES"); ecipher.init(Cipher.ENCRYPT_MODE, aesKey); return Encrypter.toHexString(ecipher.doFinal(msg.getBytes())); } catch (Exception e) { String errMsg = "decrypt error, data:" + msg; throw new EncrypterException(errMsg, e); } } public byte[] decrypt(String msg) { try { Cipher dcipher = Cipher.getInstance("AES"); dcipher.init(Cipher.DECRYPT_MODE, aesKey); return dcipher.doFinal(Encrypter.toBytes(msg)); } catch (Exception e) { String errMsg = "decrypt error, data:" + msg; throw new EncrypterException(errMsg, e); } } public String decryptAsString(String msg) { return new String(this.decrypt(msg)); } private static SecretKey loadAesKey() { String buffer = new String(Base64.decodeBase64(aesKeyStr)); byte[] keyStr = Encrypter.toBytes(buffer); SecretKeySpec aesKey = new SecretKeySpec(keyStr, "AES"); return aesKey; } private static SecretKey loadAesKey(String aesKeyStr) { String buffer = new String(Base64.decodeBase64(aesKeyStr)); byte[] keyStr = Encrypter.toBytes(buffer); SecretKeySpec aesKey = new SecretKeySpec(keyStr, "AES"); return aesKey; } } 程序的输出如下: ac8f63257d3d0e85844b4d74269bd153 qq:1515308 加密解密是OK的。 分享牛系列,分享牛专栏,分享牛。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) Java架构师交流群 523988350 qq:1515308
dubbo 管控台可以对注册到 zookeeper 注册中心的服务或服务消费者进行管理,分享牛系列,分享牛专栏,分享牛。但管控台是否正常对 Dubbo 服务没有影响,管控台也不需要高可用,因此可以单节点部署。分享牛系列,分享牛专栏,分享牛。 IP: 192.168.116.129 部署容器:apache-tomcat-8.0.28.tar.gz 端口:8080 1、 下载最新版的 Tomcat8: 2、 解压: $ tar -zxvf apache-tomcat-8.0.28.tar $ mv apache-tomcat-8.0.28 dubbo-admin-tomcat 3、 移除dubbo-admin-tomcat/webapps 目录下的所有文件$ rm -rf * 4、 上传 Dubbo 管理控制台程序 dubbo-admin-2.5.3.war 到dubbo-admin-tomcat/webapps 解压并把目录命名为 ROOT: $ unzip dubbo-admin-2.5.3.war -d ROOT 把 dubbo-admin-2.5.3.war 移到/home/wusc/tools 目录备份 $ rm -rf dubbo-admin-2.5.3.war (因为tomcat启动的时候回去解压war包) 6、 配置 dubbo.properties: $ vi ROOT/WEB-INF/dubbo.properties dubbo.registry.address=zookeeper://192.168.116.129:2181 dubbo.admin.root.password=shareniu dubbo.admin.guest.password=shareniu (以上密码在正式上生产前要修改)注意如果你是root用户的话,这里不生效默认的用户名密码就是root用户的账号密码 7、 防火墙开启 8080 端口,用 root 用户修改/etc/sysconfig/iptables, # vi /etc/sysconfig/iptables 增加: ## dubbo-admin-tomcat:8080 -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT 重启防火墙: # service iptables restart 8、 启动 Tomat /usr/shareniu/dubbo-admin-tomcat/bin/startup.sh 。 9、 浏览http://192.168.116.129:8080/ ok搞定了 分享牛系列,分享牛专栏,分享牛。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
An internal error occurred during: "Retrieving archetypes:".GC overhead limit exceeded 异常,分享牛系列,分享牛专栏,分享牛。 出现这种情况是什么原因造成的呢? 1.肯定是jvm抛出的异常。 2.eclipse或者myeclipse配置的文件在安装的目录下myeclipse.ini或者eclipse.ini。所有能配置的大概就在这个地方了。 3.修改文件如下: -vmargs-Xmx1024m-Xms512m-XX:MaxPermSize=1024m ok了,具体的参数的可以可以参考之前的jvm系列的文章。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
java类加载机制,java对象实例化机制,分享牛博客。分享牛,分享牛原创。 更多参考http://blog.csdn.net/qq_30739519/article/details/51415757一文。 下面声明类看看测试结果: package com.shareniu; public class HelloA { { System.out.println("before class HelloA"); } public HelloA() { System.out.println("HelloA"); } static{ System.out.println("static HelloA"); } { System.out.println("after class HelloA"); } } public class HelloB extends HelloA { private int i; public HelloB() { System.out.println("HelloB"); } { System.out.println(i); System.out.println(" before class HelloB"); } { System.out.println("after class HelloB"); } static{ System.out.println("static HelloB"); } public static void main(String[] args) { new HelloB(); System.out.println("shareniu"); new HelloB(); } 输出的结果是什么呢? static HelloA static HelloB before class HelloA after class HelloA HelloA 0 before class HelloB after class HelloB HelloB shareniu before class HelloA after class HelloA HelloA 0 before class HelloB after class HelloB HelloB 1.1.1. 总结 1.实例化类对象的时候,先实例化父类的静态方法,再实例化子类的静态方法,而且只会执行一次。 2.静态块的代码的优先级是最高的。 3.先初始化父类的代码块,再执行父类的构造方法。继续初始化自己的代码块,再执行自己的构造方法。 4.代码块是优先于构造方法执行的。这里就是类对象初始化的2次构造机制造成的。先执行代码块初始化所有的变量信息,构造方法执行的时候,如果有变量的赋值,就去再次初始化已经修改的值的内存。 5.永远都是先加载父类,在加载子类。先加载static的,在加载类对象的变量信息。 6.代码块{}的执行顺序,从上而下执行。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
1.1. 单例模式 写设计模式的时候,我在思考为什么要写设计模式,正如鲁迅先生说的:世界上本来是没有路的,走得多了就有路了。设计模式也是先生所讲的一样,别人已经发明了一个轮子,后人只需要使用即可。模式就是解决某一类问题的。分享牛系列,分享牛专栏,分享牛。 单例模式:为什么需要单例模式呢?很简单单例模式本质就是控制类的实例的个数。假如我们需要一个工具类去读取文件,很显然只需要一个类就可以了,因为多个类把文件内容一次性加载到内存而且是重复的是没有意义的。这个就是典型的单例模式需要解决的问题。分享牛系列,分享牛专栏,分享牛。 扩展:典型的LRU算法,我们怎么实现呢。这个就是单例模式的延伸,控制类的实例的个数,我们可以控制类的实例的个数,不在局限于单一的一个。本质都是一样控制类的实例的个数。分享牛系列,分享牛专栏,分享牛。 下面开始单例模式代码的书写吧? 1.2. 实现方式一 很简单的一个例子,既然单例模式控制的是类的实例的个数,那我们就不让外部实例化,怎么不让外部实例化呢?我们可以把类的构造方法私有即可,在这里不考虑反射的使用。不让外部实例化,而且还要让外部的其他客户使用,那我们就提供一个static的方法,让外部使用吧。 package com.shareniu.singleton; public class Singleton1 { private Singleton1() { super(); } private static Singleton1 instance=new Singleton1(); public static Singleton1 getInstance() { return instance; } public static void main(String[] args) { Singleton1 instance1 = Singleton1.getInstance(); Singleton1 instance2 = Singleton1.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } } 1.3. 实现方式二 package com.shareniu.singleton; public class Singleton2 { private Singleton2() { } private static Singleton2 instance; public static Singleton2 getInstance() { if (instance==null) { instance=new Singleton2(); } return instance; } public static void main(String[] args) { Singleton2 instance1 = Singleton2.getInstance(); Singleton2 instance2 = Singleton2.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } } if (instance==null) 第一个不存在再去获取类的实例,好像看着没问题慢但是在多线程的条件下,这种方法是有问题的?假如第一个线程执行到if (instance==null) 但是还没有实例化, 第二个线程也跟进来了,那这样不就实例化2个对象了,所以这种方式多线程有问题,而且第一次加载比较慢。 1.4. 实现方式三 在方法2的基础上,我们改造代码如下:加入synchronized 关键字,这样多线程的问题就解决了,但是每次获取实例的时候,其他线程就是等待状态,所以这一种synchronized 范围太大了。阻塞时间就长。 package com.shareniu.singleton; public class Singleton4 { private Singleton4() { } private static Singleton4 instance; public synchronized static Singleton4 getInstance() { if (instance==null) { instance=new Singleton4(); } return instance; } public static void main(String[] args) { Singleton4 instance1 = Singleton4.getInstance(); Singleton4 instance2 = Singleton4.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } } 1.5. 实现方式四 这种方式将synchronized 的范围缩小到if (instance==null) {内部 双重锁机制。 package com.shareniu.singleton; public class Singleton3 { private Singleton3() { } private static Singleton3 instance; public static Singleton3 getInstance() { if (instance==null) { synchronized (Singleton3.class) { instance=new Singleton3(); } } return instance; } public static void main(String[] args) { Singleton3 instance1 = Singleton3.getInstance(); Singleton3 instance2 = Singleton3.getInstance(); System.out.println(instance1+"---"); System.out.println(instance2+"---"); } } 1.6. 实现方式五 这种方式利用类的加载机制,静态块代码只加载一次,同时也避免了多线程的问题。建议使用这种方式。 package com.shareniu.singleton; public class Singleton5 { private Singleton5() { } private static class LazyHolder { private static final Singleton5 INSTANCE = new Singleton5(); } public synchronized static Singleton5 getInstance() { return LazyHolder.INSTANCE; } public static void main(String[] args) { Singleton5 instance1 = Singleton5.getInstance(); Singleton5 instance2 = Singleton5.getInstance(); System.out.println(instance1 + "---"); System.out.println(instance2 + "---"); } } 1.7. 总结 1.饿汉模式,不存在线程安全的问题。缺点就是可能一开始就创建了一个类的实例,比较占用内存。 2.懒汉式就是程序需要的时候再去加载,体现了延迟的思想、缺点:第一次可能慢,以后就差不多了。差不多是查多少呢,还是需要去判断在取值吧。 3.利用类的加载机制,推荐使用,类的加载机制第一不需要判断,第二解决了线程的安全问题。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
1.1 散列算法 散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。分享牛系列,分享牛专栏,分享牛 一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111和salt(盐,一个随机数)进行散列,这样虽然密码都是111111加不同的盐会生成不同的散列值。分享牛系列,分享牛专栏,分享牛 1.1.1 例子 //md5加密,不加盐 String password_md5 = new Md5Hash("111111").toString(); System.out.println("md5加密,不加盐="+password_md5); //md5加密,加盐,一次散列 String password_md5_sale_1 = new Md5Hash("111111", "eteokues", 1).toString(); System.out.println("password_md5_sale_1="+password_md5_sale_1); String password_md5_sale_2 = new Md5Hash("111111", "uiwueylm", 1).toString(); System.out.println("password_md5_sale_2="+password_md5_sale_2); //两次散列相当于md5(md5()) //使用SimpleHash String simpleHash = new SimpleHash("MD5", "111111", "eteokues",1).toString(); System.out.println(simpleHash); 1.1.2 在realm中使用 实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。 1.1.2.1 自定义realm @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //用户账号 String username = (String) token.getPrincipal(); //根据用户账号从数据库取出盐和加密后的值 //..这里使用静态数据 //如果根据账号没有找到用户信息则返回null,shiro抛出异常“账号不存在” //按照固定规则加密码结果 ,此密码 要在数据库存储,原始密码 是111111,盐是eteokues String password = "cb571f7bd7a6f73ab004a70322b963d5"; //盐,随机数,此随机数也在数据库存储 String salt = "eteokues"; //返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( username, password, ByteSource.Util.bytes(salt),getName()); return simpleAuthenticationInfo; } 1.1.2.2 realm配置 配置shiro-cryptography.ini [main] #定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #散列算法 credentialsMatcher.hashAlgorithmName=md5 #散列次数 credentialsMatcher.hashIterations=1 #将凭证匹配器设置到realm customRealm=cn.shareniu.shiro.authentication.realm.CustomRealm2 customRealm.credentialsMatcher=$credentialsMatcher securityManager.realms=$customRealm 1.1.2.3 测试代码 测试代码同上个章节,注意修改ini路径。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) Java架构师交流群 523988350
1.1 自定义Realm 上边的程序使用的是shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。分享牛系列,分享牛专栏,分享牛。 1.1.1 shiro提供的realm 最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm。 1.1.2 自定义Realm public class CustomRealm1 extends AuthorizingRealm { @Override public String getName() { return "customRealm1"; } //支持UsernamePasswordToken @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //从token中 获取用户身份信息 String username = (String) token.getPrincipal(); //拿username从数据库中查询 //.... //如果查询不到则返回null if(!username.equals("zhang")){//这里模拟查询不到 return null; } //获取从数据库查询出来的用户密码 String password = "123";//这里使用静态数据模拟。。 //返回认证信息由父类AuthenticatingRealm进行认证 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( username, password, getName()); return simpleAuthenticationInfo; } } // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { // 获取身份信息 String username = (String) principals.getPrimaryPrincipal(); // 根据身份信息从数据库中查询权限数据 //....这里使用静态数据模拟 List<String> permissions = new ArrayList<String>(); permissions.add("user:create"); permissions.add("user.delete"); //将权限信息封闭为AuthorizationInfo SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for(String permission:permissions){ simpleAuthorizationInfo.addStringPermission(permission); } return simpleAuthorizationInfo; } 1.1.3 shiro-realm.ini [main] #自定义 realm customRealm=cn.shareniu.shiro.authentication.realm.CustomRealm1 #将realm设置到securityManager securityManager.realms=$customRealm 思考:这里为什么不用配置[users]了?? shiro-permission.ini中的[roles]为什么不需要了?? 1.1.4 测试代码 测试代码同入门程序,将ini的地址修改为shiro-realm.ini。 分别模拟账号不存在、密码错误、账号和密码正确进行测试。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
1 shiro授权 shiro授权,分享牛系列,分享牛专栏,分享牛。shiro授权原理,shiro授权分析。 shiro授权 1.1 授权流程 1.2 授权方式 Shiro 支持三种方式的授权: 编程式:通过写if/else 授权代码块完成: Subject subject = SecurityUtils.getSubject(); if(subject.hasRole(“admin”)) { //有权限 } else { //无权限 } 注解式:通过在执行的Java方法上放置相应的注解完成: @RequiresRoles("admin") public void hello() { //有权限 } JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成: <shiro:hasRole name="admin"> <!— 有权限—> </shiro:hasRole> 本教程序授权测试使用第一种编程方式,实际与web系统集成使用后两种方式。 1.3 授权测试 1.3.1 shiro-permission.ini 创建存放权限的配置文件shiro-permission.ini,如下: [users] #用户zhang的密码是123,此用户具有role1和role2两个角色 zhang=123,role1,role2 wang=123,role2 [roles] #角色role1对资源user拥有create、update权限 role1=user:create,user:update #角色role2对资源user拥有create、delete权限 role2=user:create,user:delete #角色role3对资源user拥有create权限 role3=user:create 在ini文件中用户、角色、权限的配置规则是:“用户名=密码,角色1,角色2...” “角色=权限1,权限2...”,首先根据用户名找角色,再根据角色找权限,角色是权限集合。 1.3.2 权限字符串规则 权限字符串的规则是:“资源标识符:操作:资源实例标识符”,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用*通配符。 例子: 用户创建权限:user:create,或user:create:* 用户修改实例001的权限:user:update:001 用户实例001的所有权限:user:*:001 1.3.3 测试代码 测试代码同认证代码,注意ini地址改为shiro-permission.ini,主要学习下边授权的方法,注意:在用户认证通过后执行下边的授权代码。 @Test public void testPermission() { // 从ini文件中创建SecurityManager工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory( "classpath:shiro-permission.ini"); // 创建SecurityManager SecurityManager securityManager = factory.getInstance(); // 将securityManager设置到运行环境 SecurityUtils.setSecurityManager(securityManager); // 创建主体对象 Subject subject = SecurityUtils.getSubject(); // 对主体对象进行认证 // 用户登陆 // 设置用户认证的身份(principals)和凭证(credentials) UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); try { subject.login(token); } catch (AuthenticationException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 用户认证状态 Boolean isAuthenticated = subject.isAuthenticated(); System.out.println("用户认证状态:" + isAuthenticated); // 用户授权检测 基于角色授权 // 是否有某一个角色 System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1")); // 是否有多个角色 System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2"))); // subject.checkRole("role1"); // subject.checkRoles(Arrays.asList("role1", "role2")); // 授权检测,失败则抛出异常 // subject.checkRole("role22"); // 基于资源授权 System.out.println("是否拥有某一个权限:" + subject.isPermitted("user:delete")); System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user:create:1", "user:delete")); //检查权限 subject.checkPermission("sys:user:delete"); subject.checkPermissions("user:create:1","user:delete"); } 1.3.4 基于角色的授权 // 用户授权检测 基于角色授权 // 是否有某一个角色 System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1")); // 是否有多个角色 System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2"))); 对应的check方法: subject.checkRole("role1"); subject.checkRoles(Arrays.asList("role1", "role2")); 上边check方法如果授权失败则抛出异常: org.apache.shiro.authz.UnauthorizedException: Subject does not have role [.....] 1.3.5 基于资源授权 // 基于资源授权 System.out.println("是否拥有某一个权限:" + subject.isPermitted("user:delete")); System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user:create:1", "user:delete")); 对应的check方法: subject.checkPermission("sys:user:delete"); subject.checkPermissions("user:create:1","user:delete"); 上边check方法如果授权失败则抛出异常: org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [....] 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
1 shiro认证 1.1 认证流程 分享牛系列,分享牛专栏,分享牛。shiro认证分析,shiro认证原理分析。 1.2 入门程序(用户登陆和退出) 1.2.1 创建java工程 jdk版本:1.7.0_72 eclipse:elipse-indigo 1.2.2 加入shiro-core的Jar包及依赖包 1.2.3 log4j.properties日志配置文件 log4j.rootLogger=debug, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n 1.2.4 shiro.ini 通过Shiro.ini配置文件初始化SecurityManager环境。 在eclipse配置后,在classpath创建shiro.ini配置文件,为了方便测试将用户名和密码配置的shiro.ini配置文件中: [users] zhang=123 lisi=123 1.2.5 认证代码 // 用户登陆、用户退出 @Test public void testLoginLogout() { // 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境 Factory<SecurityManager> factory = new IniSecurityManagerFactory( "classpath:shiro.ini"); // 通过工厂创建SecurityManager SecurityManager securityManager = factory.getInstance(); // 将securityManager设置到运行环境中 SecurityUtils.setSecurityManager(securityManager); // 创建一个Subject实例,该实例认证要使用上边创建的securityManager进行 Subject subject = SecurityUtils.getSubject(); // 创建token令牌,记录用户认证的身份和凭证即账号和密码 UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); try { // 用户登陆 subject.login(token); } catch (AuthenticationException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 用户认证状态 Boolean isAuthenticated = subject.isAuthenticated(); System.out.println("用户认证状态:" + isAuthenticated); // 用户退出 subject.logout(); isAuthenticated = subject.isAuthenticated(); System.out.println("用户认证状态:" + isAuthenticated); } 1.2.6 认证执行流程 1、 创建token令牌,token中有用户提交的认证信息即账号和密码 2、 执行subject.login(token),最终由securityManager通过Authenticator进行认证 3、 Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带) 4、 IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。 1.2.7 常见的异常 UnknownAccountException 账号不存在异常如下: org.apache.shiro.authc.UnknownAccountException: No account found for user。。。。 IncorrectCredentialsException 当输入密码错误会抛此异常,如下: org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials. 更多如下: DisabledAccountException(帐号被禁用) LockedAccountException(帐号被锁定) ExcessiveAttemptsException(登录失败次数过多) ExpiredCredentialsException(凭证过期)等 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) java架构师交流群 523988350
1 shiro介绍 1.1 什么是shiro 分享牛系列,分享牛专栏,分享牛。shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。 1.2 为什么要学shiro 既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。 shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。 java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) 1.3 shiro架构 1.3.1 Subject Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权 1.3.2 SecurityManager SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。 SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。 1.3.3 Authenticator Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。 1.3.4 Authorizer Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。 1.3.5 realm Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。 注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。 1.3.6 sessionManager sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。 1.3.7 SessionDAO SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。 1.3.8 CacheManager CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。 1.3.9 Cryptography Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。 1.4 shiro的jar包 与其它java开源框架类似,将shiro的jar包加入项目就可以使用shiro提供的功能了。shiro-core是核心包必须选用,还提供了与web整合的shiro-web、与spring整合的shiro-spring、与任务调度quartz整合的shiro-quartz等,下边是shiro各jar包的maven坐标。 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.3</version> </dependency> 也可以通过引入shiro-all包括shiro所有的包: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency> 参考lib目录 : 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519)
1 rbac数据库设计 RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制分享牛原创,分享牛系列,分享牛。rbac 用户角色权限资源表如何设计呢?下面开始表的设计。RBAC表结构。 1.1 用户表 CREATE TABLE `sys_user` ( `id` varchar(36) NOT NULL COMMENT '主键', `usercode` varchar(32) NOT NULL COMMENT '账号', `username` varchar(64) NOT NULL COMMENT '姓名', `password` varchar(32) NOT NULL COMMENT '密码', `salt` varchar(64) DEFAULT NULL COMMENT '盐', `locked` char(1) DEFAULT NULL COMMENT '账号是否锁定,1:锁定,0未锁定', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.2 角色表 CREATE TABLE `sys_role` ( `id` varchar(36) NOT NULL, `name` varchar(128) NOT NULL, `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.3 用户角色中间表 CREATE TABLE `sys_user_role` ( `id` varchar(36) NOT NULL, `sys_user_id` varchar(32) NOT NULL, `sys_role_id` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.4 权限表 CREATE TABLE `sys_permission` ( `id` bigint(20) NOT NULL COMMENT '主键', `name` varchar(128) NOT NULL COMMENT '资源名称', `type` varchar(32) NOT NULL COMMENT '资源类型:menu,button,', `url` varchar(128) DEFAULT NULL COMMENT '访问url地址', `percode` varchar(128) DEFAULT NULL COMMENT '权限代码字符串', `parentid` bigint(20) DEFAULT NULL COMMENT '父结点id', `parentids` varchar(128) DEFAULT NULL COMMENT '父结点id列表串', `sortstring` varchar(128) DEFAULT NULL COMMENT '排序号', `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.5 角色权限表 CREATE TABLE `sys_role_permission` ( `id` varchar(36) NOT NULL, `sys_role_id` varchar(32) NOT NULL COMMENT '角色id', `sys_permission_id` varchar(32) NOT NULL COMMENT '权限id', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.6 初始化数据 /* SQLyog v10.2 MySQL - 5.1.72-community : Database - shiro ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*Data for the table `sys_permission` */ insert into `sys_permission`(`id`,`name`,`type`,`url`,`percode`,`parentid`,`parentids`,`sortstring`,`available`) values (1,'权限','','',NULL,0,'0/','0','1'),(11,'商品管理','menu','/item/queryItem.action',NULL,1,'0/1/','1.','1'),(12,'商品新增','permission','/item/add.action','item:create',11,'0/1/11/','','1'),(13,'商品修改','permission','/item/editItem.action','item:update',11,'0/1/11/','','1'),(14,'商品删除','permission','','item:delete',11,'0/1/11/','','1'),(15,'商品查询','permission','/item/queryItem.action','item:query',11,'0/1/15/',NULL,'1'),(21,'用户管理','menu','/user/query.action','user:query',1,'0/1/','2.','1'),(22,'用户新增','permission','','user:create',21,'0/1/21/','','1'),(23,'用户修改','permission','','user:update',21,'0/1/21/','','1'),(24,'用户删除','permission','','user:delete',21,'0/1/21/','','1'); /*Data for the table `sys_role` */ insert into `sys_role`(`id`,`name`,`available`) values ('ebc8a441-c6f9-11e4-b137-0adc305c3f28','商品管理员','1'),('ebc9d647-c6f9-11e4-b137-0adc305c3f28','用户管理员','1'); /*Data for the table `sys_role_permission` */ insert into `sys_role_permission`(`id`,`sys_role_id`,`sys_permission_id`) values ('ebc8a441-c6f9-11e4-b137-0adc305c3f21','ebc8a441-c6f9-11e4-b137-0adc305c','12'),('ebc8a441-c6f9-11e4-b137-0adc305c3f22','ebc8a441-c6f9-11e4-b137-0adc305c','11'),('ebc8a441-c6f9-11e4-b137-0adc305c3f24','ebc9d647-c6f9-11e4-b137-0adc305c','21'),('ebc8a441-c6f9-11e4-b137-0adc305c3f25','ebc8a441-c6f9-11e4-b137-0adc305c','15'),('ebc9d647-c6f9-11e4-b137-0adc305c3f23','ebc9d647-c6f9-11e4-b137-0adc305c','22'),('ebc9d647-c6f9-11e4-b137-0adc305c3f26','ebc8a441-c6f9-11e4-b137-0adc305c','13'); /*Data for the table `sys_user` */ insert into `sys_user`(`id`,`usercode`,`username`,`password`,`salt`,`locked`) values ('lisi','lisi','李四','bf07fd8bbc73b6f70b8319f2ebb87483','uiwueylm','0'),('zhangsan','zhangsan','张三','cb571f7bd7a6f73ab004a70322b963d5','eteokues','0'); /*Data for the table `sys_user_role` */ insert into `sys_user_role`(`id`,`sys_user_id`,`sys_role_id`) values ('ebc8a441-c6f9-11e4-b137-0adc305c3f28','zhangsan','ebc8a441-c6f9-11e4-b137-0adc305c'),('ebc9d647-c6f9-11e4-b137-0adc305c3f28','lisi','ebc9d647-c6f9-11e4-b137-0adc305c'); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519)
1 权限管理 1.1 什么是权限管理 分享牛原创,分享牛系列。基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。 1.2 用户身份认证 1.2.1 概念 身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) 1.2.2 用户名密码身份认证流程 1.2.3 关键对象 上边的流程图中需要理解以下关键对象: 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) Subject:主体 访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体; n Principal:身份信息 是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。 n credential:凭证信息 是只有主体自己知道的安全信息,如密码、证书等。 1.3 授权 1.3.1 概念 授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。 1.3.2 授权流程 下图中橙色为授权流程。 1.3.3 关键对象 授权可简单理解为who对what(which)进行How操作: n Who,即主体(Subject),主体需要访问系统中的资源。 n What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。 n How,权限/许可(Permission),规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。 权限分为粗颗粒和细颗粒,粗颗粒权限是指对资源类型的权限,细颗粒权限是对资源实例的权限。 主体、资源、权限关系如下图: 1.3.4 权限模型 对上节中的主体、资源、权限通过数据模型表示。 主体(账号、密码) 资源(资源名称、访问地址) 权限(权限名称、资源id) 角色(角色名称) 角色和权限关系(角色id、权限id) 主体和角色关系(主体id、角色id) 如下图: 通常企业开发中将资源和权限表合并为一张权限表,如下: 资源(资源名称、访问地址) 权限(权限名称、资源id) 合并为: 权限(权限名称、资源名称、资源访问地址) 上图常被称为权限管理的通用模型,不过企业在开发中根据系统自身的特点还会对上图进行修改,但是用户、角色、权限、用户角色关系、角色权限关系是需要去理解的。 1.3.5 权限分配 对主体分配权限,主体只允许在权限范围内对资源进行操作,比如:对u01用户分配商品修改权限,u01用户只能对商品进行修改。 权限分配的数据通常需要持久化,根据上边的数据模型创建表并将用户的权限信息存储在数据库中。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) 1.3.6 权限控制 用户拥有了权限即可操作权限范围内的资源,系统不知道主体是否具有访问权限需要对用户的访问进行控制。 1.3.6.1 基于角色的访问控制 RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下: 上图中的判断逻辑代码可以理解为: if(主体.hasRole("总经理角色id")){ 查询工资 } 缺点:以角色进行访问控制粒度较粗,如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断主体的角色是否是总经理或部门经理”,系统可扩展性差。 修改代码如下: if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){ 查询工资 } 1.3.6.2 基于资源的访问控制 RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制,比如:主体必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下: 上图中的判断逻辑代码可以理解为: if(主体.hasPermission("查询工资权限标识")){ 查询工资 } 优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。 2 权限管理解决方案 2.1 粗颗粒度和细颗粒度 2.1.1 什么是粗颗粒度和细颗粒度 对资源类型的管理称为粗颗粒度权限管理,即只控制到菜单、按钮、方法,粗粒度的例子比如:用户具有用户管理的权限,具有导出订单明细的权限。对资源实例的控制称为细颗粒度权限管理,即控制到数据级别的权限,比如:用户只允许修改本部门的员工信息,用户只允许导出自己创建的订单明细。 2.1.2 如何实现粗颗粒度和细颗粒度 对于粗颗粒度的权限管理可以很容易做系统架构级别的功能,即系统功能操作使用统一的粗颗粒度的权限管理。 对于细颗粒度的权限管理不建议做成系统架构级别的功能,因为对数据级别的控制是系统的业务需求,随着业务需求的变更业务功能变化的可能性很大,建议对数据级别的权限控制在业务层个性化开发,比如:用户只允许修改自己创建的商品信息可以在service接口添加校验实现,service接口需要传入当前操作人的标识,与商品信息创建人标识对比,不一致则不允许修改商品信息。
eclipse properties 插件安装,分享牛,分享牛原创。eclipse properties 编辑器使用。 eclipse因为是原生的,可能集成的插件不多,需要自己手动安装。eclipse properties插件的坐标为: Name: Properties Editor Location: http://propedit.sourceforge.jp/eclipse/updates/ 添加安装即可。一般选择第一个,是最新的。 在程序中设置为默认的编辑器即可。 离线安装的方式可以到http://propedit.sourceforge.jp/index_en.html官网下载自己需要的版本即可。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519)
摘要:分享牛,分享牛系列, Unable to ignore resources Attempted to beginRule: 异常信息处理。 出现Unable to ignore resources Attempted to beginRule异常情况。 出现的情况如下图所示: 解决方案如下图所示: ok了,完美解决。 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519)