提交 82926a2b 编写于 作者: J Joram Barrez

Merge branch 'master' of https://github.com/Activiti/Activiti

......@@ -4,7 +4,7 @@
<property file="${user.home}/.activiti/build.properties" />
<property name="activiti.version" value="5.15-SNAPSHOT" />
<property name="activiti.version" value="5.15" />
<property name="target.distro.root" value="target/zip/activiti-${activiti.version}" />
<property name="activiti.website" value="../../activiti-website" />
......@@ -35,7 +35,7 @@
<equals arg1="${nodocs}" arg2="true" />
</condition>
<exec executable="${mvn.executable}" dir=".." failonerror="true">
<env key="MAVEN_OPTS" value="-Xmx1024m -Xms512m" />
<env key="MAVEN_OPTS" value="-Xmx1024m -Xms512m -XX:MaxPermSize=256M" />
<arg line="-Pdistro${nodocs.profile} clean install -Dmaven.test.skip=true" />
</exec>
</target>
......
......@@ -46,17 +46,17 @@ This software package includes changed source code of the following libraries:
JUEL
* Location: http://juel.sourceforge.net/
* Included in activiti-engine-5.15-SNAPSHOT.jar in package org.activiti.engine.impl.juel
* Included in activiti-engine-5.15.jar in package org.activiti.engine.impl.juel
* License: Apache V2
Quartz
* Location: http://www.quartz-scheduler.org/
* CronExpression is included in activiti-engine-5.15-SNAPSHOT.jar in package org.activiti.engine.impl.calendar
* CronExpression is included in activiti-engine-5.15.jar in package org.activiti.engine.impl.calendar
* License: Apache V2
JSON
* Location: http://www.JSON.org/java
* Included in activiti-engine-5.15-SNAPSHOT.jar in package org.activiti.engine.impl.json
* Included in activiti-engine-5.15.jar in package org.activiti.engine.impl.json
* License:
===============================================================================
Copyright (c) 2002 JSON.org
......
......@@ -35,6 +35,214 @@
<h1>Activiti Release Notes</h1>
<h3>Release Notes - Activiti - Version 5.15</h3>
<h4>Highlights</h4>
<ul>
<li>Multi tanancy support added to Activiti, including the Java and REST API.</li>
<li>Added new event support to listen for events in the Activiti Engine, like task deleted, variable updated, process engine created and many more.</li>
<li>Introduction of data object support (thanks to Lori Small and team)</li>
<li>Improved Spring support with great and easy to use annotations (thanks to Josh Long)</li>
<li>Added an easy way to do custom sql execution (<a href="http://www.jorambarrez.be/blog/2014/01/17/execute-custom-sql-in-activiti/">http://www.jorambarrez.be/blog/2014/01/17/execute-custom-sql-in-activiti/</a>)</li>
<li>Improved OSGi support + added OSGi unit testing using Tinybundles</li>
<li>Various fixes and improvements</li>
</ul>
<h4>Bug fixes and various smaller improvements</h4>
<p>Check out the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=12091&version=19344">Release notes</a> for more details</p>
<h2> Sub-task
</h2>
<ul>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1608'>ACT-1608</a>] - Fix DB2 metadata problem
</li>
</ul>
<h2> Bug
</h2>
<ul>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1339'>ACT-1339</a>] - MultipleInstance UserTask does not use AtomicOperation -&gt; CDI Event Listener fails
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1549'>ACT-1549</a>] - endTime of joining parallel gateway is not set: HistoricActivityInstance.getEndTime() returns null
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1589'>ACT-1589</a>] - NPE when executing SignalThrowingEvent
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1610'>ACT-1610</a>] - DbSqlSession isTablePresent method adds table prefix which causes check to fail
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1627'>ACT-1627</a>] - Save task will throw assignment-event even if assignee is not altered
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1712'>ACT-1712</a>] - Duplicate task when signal is sent to User Task
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1733'>ACT-1733</a>] - REST API documentation for Task Query points to wrong URL
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1745'>ACT-1745</a>] - ProcessDiagramGenerator misses some diagram flow elements and truncates labels
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1752'>ACT-1752</a>] - In BpmnDeployer, only schedule start timers AFTER process definition has been persisted
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1794'>ACT-1794</a>] - LDAP - Group lookups for a user fail if the DN has special characters
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1795'>ACT-1795</a>] - lineChart report hasn&#39;t been rendered
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1816'>ACT-1816</a>] - ManagementService doesn&#39;t seem to give actual table Name for EventSubscriptionEntity.class
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1822'>ACT-1822</a>] - MultiInstance loopIndexVariable support
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1823'>ACT-1823</a>] - CancelEndEvent goes into dead lock
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1825'>ACT-1825</a>] - Infinite recursion in TestActivityBehaviorFactory
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1826'>ACT-1826</a>] - OSGI bundle activiti-engine/5.14 failed to deploy due to duplicated imported org.activiti.osgi + WorkAround
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1828'>ACT-1828</a>] - Completing a task in DelegationState.PENDING does not throw ActivitiException
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1838'>ACT-1838</a>] - Activiti 5.14 did not ship incremental upgrade DB schema migration script (to auto upgrade 5.13 -&gt; 5.14)
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1839'>ACT-1839</a>] - ACT_FK_VAR_BYTEARRAY violated on variable update
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1842'>ACT-1842</a>] - Add taskService.complete() method that takes an option boolean to determine the scope of variables
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1844'>ACT-1844</a>] - ActivitiRule fails if test methods are declared in a super class
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1848'>ACT-1848</a>] - ClassCastException when using CdiEventSupportBpmnParseHandler with multi instance user task
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1854'>ACT-1854</a>] - ExtensionElements parsing causes NullpointerException in non Process/Activity Context
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1858'>ACT-1858</a>] - Groovy generated classes aren&#39;t garbage collected
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1859'>ACT-1859</a>] - TaskListeners configured for ALL event types do not receive DELETE events
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1863'>ACT-1863</a>] - NPE on HistoricVariableInstanceQuery
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1872'>ACT-1872</a>] - Text Annotation is not generated in export and in viewer
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1879'>ACT-1879</a>] - Job Executor job acquisition can lead to deadlocks in clustered setup
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1881'>ACT-1881</a>] - activiti-engine OSGi bundle requires junit classes
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1882'>ACT-1882</a>] - activiti-spring OSGi bundle requires spring-test classes.
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1883'>ACT-1883</a>] - Listener end event is not notified when compensation done
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1884'>ACT-1884</a>] - add missing type of serviceTask in activiti-bpmn-extensions-5.15.xsd
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1887'>ACT-1887</a>] - 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
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1888'>ACT-1888</a>] - UserTask XML converter error when it has default sequence flow
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1889'>ACT-1889</a>] - Boundary event don&#39;t follow pool position.
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1891'>ACT-1891</a>] - After edit or click form properties typed enum lose values
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1892'>ACT-1892</a>] - Repeat add listener
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1894'>ACT-1894</a>] - the parameter name of HistoricActivityInstanceQuery.activityInstanceId() should be activityInstanceId, not processInstanceId
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1896'>ACT-1896</a>] - Using activityId(..) and processInstanceBusinessKey(.., true) on Execution-query causes SQL-exception
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1897'>ACT-1897</a>] - Form-properties (and some other table-based properties) no longer selectable/editable/removable in Designer 5.14.0
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1901'>ACT-1901</a>] - The coordinates of activity nodes contained in a DiagramLayout are sometime not correct.
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1908'>ACT-1908</a>] - Activiti designer 5.14 removes custom form property information
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1909'>ACT-1909</a>] - REST queries should support paging like their normal GET counterparts do
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1912'>ACT-1912</a>] - Task Listener Bug
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1914'>ACT-1914</a>] - Activiti designer does not update sequence flow references
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1918'>ACT-1918</a>] - deploying a process fails with hibernate 4.2.6.Final &quot;The object is already closed&quot;
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1923'>ACT-1923</a>] - Setting task assignee and using updateTask() does not update task history
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1927'>ACT-1927</a>] - Manual tasks are actually created as generic tasks in the BPMN source
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1928'>ACT-1928</a>] - Changing the id of elements in the Properties view does not change references to it leading to broken models
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1929'>ACT-1929</a>] - Co-ordinates of BPMNEdge labels are relative to the wrong origin
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1930'>ACT-1930</a>] - Activiti designer 5.14 form editor prevents editing of fields
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1935'>ACT-1935</a>] - BeanELResolver hides target exception in ELException when catching InvocationTargetException
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1937'>ACT-1937</a>] - StackOverflowError when using an EndErrorEvent from an Event Sub-Process
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1939'>ACT-1939</a>] - HistoryService loads invalid task local variables for completed task
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1940'>ACT-1940</a>] - Possible bug in MS-SQL server configuration with schema
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1941'>ACT-1941</a>] - IdentityService interface leaking implementation details
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1944'>ACT-1944</a>] - LDAP-Authentication fails if LDAP-User has no forename
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1949'>ACT-1949</a>] - Jobs are not being removed from ACT_RU_JOB for for &lt;timeCycle&gt; timers
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1951'>ACT-1951</a>] - ACT_RU_JOBS has orphaned rows after deleting Process with repeat timers
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1953'>ACT-1953</a>] - multi instance sub process,variable conflict?
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1491'>ACT-1491</a>] - Parsing association, drawing Annotation and connections
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1496'>ACT-1496</a>] - Replace JSON.org with GSON dependencies
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1781'>ACT-1781</a>] - Diagram improvement: curved flows, annotations, associations, label position.
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1860'>ACT-1860</a>] - Remove unique constraint on business key
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1867'>ACT-1867</a>] - MySQL DATETIME and TIMESTAMP precision
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1911'>ACT-1911</a>] - Adopt MyBatis 3.2.4
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1916'>ACT-1916</a>] - Double-check all REST-resources for explicit authenticate() call, to improve overall security
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1952'>ACT-1952</a>] - Refactor process definition validation + allow to plugin in custom validation
</li>
</ul>
<h2> New Feature
</h2>
<ul>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1448'>ACT-1448</a>] - Support for expressions in candidate/assignee/...
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1797'>ACT-1797</a>] - Send events for major state changes of domain objects
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1840'>ACT-1840</a>] - Allow to set a &#39;category&#39; on tasks
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1890'>ACT-1890</a>] - Multi tenancy support
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1898'>ACT-1898</a>] - Allow to execute custom SQL
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1904'>ACT-1904</a>] - Introduce Process Engine Configurator concept
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1907'>ACT-1907</a>] - Add ProcessInstanceHistoryLog
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1910'>ACT-1910</a>] - Introduce @EnableActiviti for Spring
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1933'>ACT-1933</a>] - Delete Identity Link from RuntimeService IdentityLink list
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1936'>ACT-1936</a>] - Query Tasks by process category
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1954'>ACT-1954</a>] - Warning Date on Task
</li>
</ul>
<h2> Task
</h2>
<ul>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1631'>ACT-1631</a>] - Deploy artifacts to central maven repository
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1833'>ACT-1833</a>] - get BusinessCalendar from BusinessCalendarManager except new a instance
</li>
<li>[<a href='http://jira.codehaus.org/browse/ACT-1869'>ACT-1869</a>] - Document signal event scope
</li>
</ul>
<h3>Release Notes - Activiti - Version 5.14</h3>
<h4>Highlights</h4>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<build>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -12,7 +12,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<dependencies>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<dependencies>
......
......@@ -49,7 +49,7 @@ package org.activiti.engine;
public interface ProcessEngine extends EngineServices {
/** the version of the activiti library */
public static String VERSION = "5.15-SNAPSHOT";
public static String VERSION = "5.15";
/** The name as specified in 'process-engine-name' in
* the activiti.cfg.xml configuration file.
......
package org.activiti.engine.impl.calendar;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.impl.calendar.CycleBusinessCalendar;
import org.activiti.engine.runtime.ClockReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An Activiti BusinessCalendar for cycle based schedules that takes into
* account a different daylight savings time zone than the one that the server
* is configured for.
* <p>
* For CRON strings DSTZONE is used as the time zone that the CRON schedule
* refers to. Leave it out to use the server time zone.
* <p>
* For ISO strings the time zone offset for the date/time specified is part of
* the string itself. DSTZONE is used to determine what the offset should be
* NOW, which may be different than when the workflow was scheduled if it is
* scheduled to run across a DST event.
*
* <pre>
* For example:
* R/2013-10-01T20:30:00/P1D DSTZONE:US/Eastern
* R/2013-10-01T20:30:00/P1D DSTZONE:UTC
* R/2013-10-01T20:30:00/P1D DSTZONE:US/Arizona
* 0 30 20 ? * MON,TUE,WED,THU,FRI * DSTZONE:US/Eastern
* 0 30 20 ? * MON,TUE,WED,THU,FRI * DSTZONE:UTC
* 0 30 20 ? * MON,TUE,WED,THU,FRI * DSTZONE:US/Arizona
* </pre>
*
* Removing the DSTZONE key will cause Activiti to use the server's time zone.
* This is the original behavior.
* <p>
* Schedule strings are versioned. Version 1 strings will use the original
* Activiti CycleBusinessCalendar. All new properties are ignored. Version 2
* strings will use the new daylight saving time logic.
*
* <pre>
* For example:
* R/2013-10-01T20:30:00/P1D VER:2 DSTZONE:US/Eastern
* 0 30 20 ? * MON,TUE,WED,THU,FRI * VER:1 DSTZONE:US/Arizona
* </pre>
*
* By default (if no VER key is included in the string), it assumes version 2.
* This can be changed by modifying the defaultScheduleVersion property.
* <p>
*
* @author mseiden
*/
public class AdvancedCycleBusinessCalendar extends CycleBusinessCalendar {
private Integer defaultScheduleVersion;
private static final Integer DEFAULT_VERSION = 2;
private static final Logger logger = LoggerFactory.getLogger(AdvancedCycleBusinessCalendar.class);
private static final Map<Integer, AdvancedSchedulerResolver> resolvers;
static {
resolvers = new ConcurrentHashMap<Integer, AdvancedSchedulerResolver>();
resolvers.put(1, new AdvancedSchedulerResolverWithoutTimeZone());
resolvers.put(2, new AdvancedSchedulerResolverWithTimeZone());
}
public AdvancedCycleBusinessCalendar(ClockReader clockReader) {
super(clockReader);
}
public AdvancedCycleBusinessCalendar(ClockReader clockReader, Integer defaultScheduleVersion) {
this(clockReader);
this.defaultScheduleVersion = defaultScheduleVersion;
}
public Integer getDefaultScheduleVersion() {
return defaultScheduleVersion == null ? DEFAULT_VERSION : defaultScheduleVersion;
}
public void setDefaultScheduleVersion(Integer defaultScheduleVersion) {
this.defaultScheduleVersion = defaultScheduleVersion;
}
@Override
public Date resolveDuedate(String duedateDescription) {
logger.info("Resolving Due Date: " + duedateDescription);
String timeZone = getValueFrom("DSTZONE", duedateDescription);
String version = getValueFrom("VER", duedateDescription);
// START is a legacy value that is no longer used, but may still exist in
// deployed job schedules
// Could be used in the future as a start date for a CRON job
// String startDate = getValueFrom("START", duedateDescription);
duedateDescription = removeValueFrom("VER", removeValueFrom("START", removeValueFrom("DSTZONE", duedateDescription))).trim();
try {
logger.info("Base Due Date: " + duedateDescription);
Date date = resolvers.get(version == null ? getDefaultScheduleVersion() : Integer.valueOf(version)).resolve(duedateDescription, clockReader,
timeZone == null ? clockReader.getCurrentTimeZone() : TimeZone.getTimeZone(timeZone));
logger.info("Calculated Date: " + (date == null ? "Will Not Run Again" : date));
return date;
} catch (Exception e) {
throw new ActivitiIllegalArgumentException("Cannot parse duration", e);
}
}
private String getValueFrom(String field, String duedateDescription) {
int fieldIndex = duedateDescription.indexOf(field + ":");
if (fieldIndex > -1) {
int nextWhiteSpace = duedateDescription.indexOf(" ", fieldIndex);
fieldIndex += field.length() + 1;
if (nextWhiteSpace > -1) {
return duedateDescription.substring(fieldIndex, nextWhiteSpace);
} else {
return duedateDescription.substring(fieldIndex);
}
}
return null;
}
private String removeValueFrom(String field, String duedateDescription) {
int fieldIndex = duedateDescription.indexOf(field + ":");
if (fieldIndex > -1) {
int nextWhiteSpace = duedateDescription.indexOf(" ", fieldIndex);
if (nextWhiteSpace > -1) {
return duedateDescription.replace(duedateDescription.substring(fieldIndex, nextWhiteSpace), "");
} else {
return duedateDescription.substring(0, fieldIndex);
}
}
return duedateDescription;
}
}
package org.activiti.engine.impl.calendar;
import java.util.Date;
import java.util.TimeZone;
import org.activiti.engine.runtime.ClockReader;
/**
* Provides an interface for versioned due date resolvers.
*
* @author mseiden
*/
public interface AdvancedSchedulerResolver {
/**
* Resolves a due date using the specified time zone (if supported)
*
* @param duedateDescription
* An original Activiti schedule string in either ISO or CRON format
* @param clockReader
* The time provider
* @param timeZone
* The time zone to use in the calculations
* @return The due date
*/
Date resolve(String duedateDescription, ClockReader clockReader, TimeZone timeZone);
}
package org.activiti.engine.impl.calendar;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.runtime.ClockReader;
/**
* Resolves a due date taking into account the specified time zone.
*
* @author mseiden
*/
public class AdvancedSchedulerResolverWithTimeZone implements AdvancedSchedulerResolver {
@Override
public Date resolve(String duedateDescription, ClockReader clockReader, TimeZone timeZone) {
Calendar nextRun = null;
try {
if (duedateDescription.startsWith("R")) {
nextRun = new DurationHelper(duedateDescription, clockReader).getCalendarAfter(clockReader.getCurrentCalendar(timeZone));
} else {
nextRun = new CronExpression(duedateDescription, clockReader, timeZone).getTimeAfter(clockReader.getCurrentCalendar(timeZone));
}
} catch (Exception e) {
throw new ActivitiException("Failed to parse scheduler expression: " + duedateDescription, e);
}
return nextRun == null ? null : nextRun.getTime();
}
}
package org.activiti.engine.impl.calendar;
import java.util.Date;
import java.util.TimeZone;
import org.activiti.engine.impl.calendar.CycleBusinessCalendar;
import org.activiti.engine.runtime.ClockReader;
/**
* Resolves a due date using the original Activiti due date resolver. This does
* not take into account the passed time zone.
*
* @author mseiden
*/
public class AdvancedSchedulerResolverWithoutTimeZone implements AdvancedSchedulerResolver {
@Override
public Date resolve(String duedateDescription, ClockReader clockReader, TimeZone timeZone) {
return new CycleBusinessCalendar(clockReader).resolveDuedate(duedateDescription);
}
}
......@@ -30,12 +30,12 @@ public class CycleBusinessCalendar extends BusinessCalendarImpl {
if (duedateDescription.startsWith("R")) {
return new DurationHelper(duedateDescription, clockReader).getDateAfter();
} else {
CronExpression ce = new CronExpression(duedateDescription);
CronExpression ce = new CronExpression(duedateDescription, clockReader);
return ce.getTimeAfter(clockReader.getCurrentTime());
}
} catch (Exception e) {
throw new ActivitiException("Failed to parse cron expression: "+duedateDescription, e);
throw new ActivitiException("Failed to parse cron expression: " + duedateDescription, e);
}
}
......
......@@ -14,30 +14,57 @@
package org.activiti.engine.impl.calendar;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.runtime.ClockReader;
import org.joda.time.DateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import java.util.*;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.impl.util.TimeZoneUtil;
import org.activiti.engine.runtime.ClockReader;
import org.joda.time.DateTimeZone;
import org.joda.time.format.ISODateTimeFormat;
/**
* helper class for parsing ISO8601 duration format (also recurring) and computing next timer date
* helper class for parsing ISO8601 duration format (also recurring) and
* computing next timer date
*/
public class DurationHelper {
Date start;
private Calendar start;
Date end;
private Calendar end;
Duration period;
private Duration period;
boolean isRepeat;
private boolean isRepeat;
int times;
private int times;
DatatypeFactory datatypeFactory;
private DatatypeFactory datatypeFactory;
public Calendar getStart() {
return start;
}
public Calendar getEnd() {
return end;
}
public Duration getPeriod() {
return period;
}
public boolean isRepeat() {
return isRepeat;
}
public int getTimes() {
return times;
}
protected ClockReader clockReader;
......@@ -51,7 +78,7 @@ public class DurationHelper {
}
if (expression.get(0).startsWith("R")) {
isRepeat = true;
times = expression.get(0).length() == 1 ? Integer.MAX_VALUE : Integer.parseInt(expression.get(0).substring(1));
times = expression.get(0).length() == 1 ? Integer.MAX_VALUE : Integer.parseInt(expression.get(0).substring(1));
expression = expression.subList(1, expression.size());
}
......@@ -64,61 +91,80 @@ public class DurationHelper {
period = parsePeriod(expression.get(1));
} else {
end = parseDate(expression.get(1));
period = datatypeFactory.newDuration(end.getTime()-start.getTime());
period = datatypeFactory.newDuration(end.getTimeInMillis() - start.getTimeInMillis());
}
}
if (start == null && end == null) {
start = clockReader.getCurrentTime();
start = clockReader.getCurrentCalendar();
}
}
public Date getDateAfter() {
public Calendar getCalendarAfter() {
return getCalendarAfter(clockReader.getCurrentCalendar());
}
public Calendar getCalendarAfter(Calendar time) {
if (isRepeat) {
return getDateAfterRepeat(clockReader.getCurrentTime());
return getDateAfterRepeat(time);
}
//TODO: is this correct?
// TODO: is this correct?
if (end != null) {
return end;
}
return add(start, period);
}
public int getTimes() {
return times;
public Date getDateAfter() {
Calendar date = getCalendarAfter();
return date == null ? null : date.getTime();
}
private Date getDateAfterRepeat(Date date) {
private Calendar getDateAfterRepeat(Calendar date) {
if (start != null) {
Date cur = start;
for (int i=0;i<times && !cur.after(date);i++) {
Calendar cur = TimeZoneUtil.convertToTimeZone(start, date.getTimeZone());
for (int i = 0; i < times && !cur.after(date); i++) {
cur = add(cur, period);
}
return cur.before(date) ? null : cur;
return cur.before(date) ? null : TimeZoneUtil.convertToTimeZone(cur, clockReader.getCurrentTimeZone());
}
Date cur = add(end, period.negate());
Date next = end;
for (int i=0;i<times && cur.after(date);i++) {
Calendar cur = add(TimeZoneUtil.convertToTimeZone(end, date.getTimeZone()), period.negate());
Calendar next = TimeZoneUtil.convertToTimeZone(end, date.getTimeZone());
for (int i = 0; i < times && cur.after(date); i++) {
next = cur;
cur = add(cur, period.negate());
}
return next.before(date) ? null : next;
return next.before(date) ? null : TimeZoneUtil.convertToTimeZone(next, clockReader.getCurrentTimeZone());
}
private Date add(Date date, Duration duration) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
duration.addTo(calendar);
return calendar.getTime();
private Calendar add(Calendar date, Duration duration) {
Calendar calendar = (Calendar) date.clone();
// duration.addTo does not account for daylight saving time (xerces),
// reversing order of addition fixes the problem
calendar.add(Calendar.SECOND, duration.getSeconds() * duration.getSign());
calendar.add(Calendar.MINUTE, duration.getMinutes() * duration.getSign());
calendar.add(Calendar.HOUR, duration.getHours() * duration.getSign());
calendar.add(Calendar.DAY_OF_MONTH, duration.getDays() * duration.getSign());
calendar.add(Calendar.MONTH, duration.getMonths() * duration.getSign());
calendar.add(Calendar.YEAR, duration.getYears() * duration.getSign());
return calendar;
}
private Date parseDate(String date) throws Exception {
return DateTime.parse(date).toDate();
private Calendar parseDate(String date) throws Exception {
return ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.forTimeZone(clockReader.getCurrentTimeZone())).parseDateTime(date).toCalendar(null);
}
private Duration parsePeriod(String period) throws Exception {
return datatypeFactory.newDuration(period);
return datatypeFactory.newDuration(period);
}
private boolean isDuration(String time) {
......
......@@ -12,32 +12,59 @@
*/
package org.activiti.engine.impl.util;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
* @author Joram Barrez
*/
public class DefaultClockImpl implements org.activiti.engine.runtime.Clock {
private static volatile Date CURRENT_TIME = null;
private static volatile Calendar CURRENT_TIME = null;
@Override
public void setCurrentTime(Date currentTime) {
this.CURRENT_TIME = currentTime;
Calendar time = null;
if (currentTime != null) {
time = new GregorianCalendar();
time.setTime(currentTime);
}
setCurrentCalendar(time);
}
@Override
public void setCurrentCalendar(Calendar currentTime) {
CURRENT_TIME = currentTime;
}
@Override
public void reset() {
this.CURRENT_TIME = null;
CURRENT_TIME = null;
}
@Override
public Date getCurrentTime() {
if (CURRENT_TIME != null) {
return CURRENT_TIME;
}
return new Date();
return CURRENT_TIME == null ? new Date() : CURRENT_TIME.getTime();
}
@Override
public Calendar getCurrentCalendar() {
return CURRENT_TIME == null ? new GregorianCalendar() : (Calendar)CURRENT_TIME.clone();
}
@Override
public Calendar getCurrentCalendar(TimeZone timeZone) {
return TimeZoneUtil.convertToTimeZone(getCurrentCalendar(), timeZone);
}
@Override
public TimeZone getCurrentTimeZone() {
return getCurrentCalendar().getTimeZone();
}
}
package org.activiti.engine.impl.util;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class TimeZoneUtil {
public static Calendar convertToTimeZone(Calendar time, TimeZone timeZone) {
Calendar foreignTime = new GregorianCalendar(timeZone);
foreignTime.setTimeInMillis(time.getTimeInMillis());
return foreignTime;
}
}
package org.activiti.engine.runtime;
import java.util.Calendar;
import java.util.Date;
/**
* This interface provides full access to the clock
*/
public interface Clock extends ClockReader{
public interface Clock extends ClockReader {
void setCurrentTime(Date currentTime);
void setCurrentCalendar(Calendar currentTime);
void reset();
}
package org.activiti.engine.runtime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* This interface provides clock reading functionality
......@@ -9,4 +11,10 @@ public interface ClockReader {
Date getCurrentTime();
Calendar getCurrentCalendar();
Calendar getCurrentCalendar(TimeZone timeZone);
TimeZone getCurrentTimeZone();
}
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
);
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
);
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
);
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
);
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -6,10 +6,10 @@ create table ACT_GE_PROPERTY (
);
insert into ACT_GE_PROPERTY
values ('schema.version', '5.15-SNAPSHOT', 1);
values ('schema.version', '5.15', 1);
insert into ACT_GE_PROPERTY
values ('schema.history', 'create(5.15-SNAPSHOT)', 1);
values ('schema.history', 'create(5.15)', 1);
insert into ACT_GE_PROPERTY
values ('next.dbid', '1', 1);
......
......@@ -59,4 +59,4 @@ alter table ACT_RE_PROCDEF
Call Sysproc.admin_cmd ('REORG TABLE ACT_RE_PROCDEF');
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -33,6 +33,6 @@ alter table ACT_HI_TASKINST
Call Sysproc.admin_cmd ('REORG TABLE ACT_HI_TASKINST');
alter table ACT_HI_ACTINST alter column ASSIGNEE_ varchar(255);
alter table ACT_HI_ACTINST alter column ASSIGNEE_ SET DATA TYPE varchar(255);
Call Sysproc.admin_cmd ('REORG TABLE ACT_HI_ACTINST');
\ No newline at end of file
......@@ -35,4 +35,4 @@ alter table ACT_RE_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -35,4 +35,4 @@ alter table ACT_RE_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -112,4 +112,4 @@ alter table ACT_RE_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -101,5 +101,5 @@ alter table ACT_HI_TASKINST
add TENANT_ID_ varchar(255) default '';
alter table ACT_HI_ACTINST
alter column ASSIGNEE_ varchar(255);
MODIFY ASSIGNEE_ varchar(255);
......@@ -46,4 +46,4 @@ alter table ACT_RE_PROCDEF
add constraint ACT_UNIQ_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -23,4 +23,4 @@ alter table ACT_HI_TASKINST
add TENANT_ID_ varchar(255) default '';
alter table ACT_HI_ACTINST
alter column ASSIGNEE_ varchar(255);
\ No newline at end of file
modify ASSIGNEE_ varchar(255);
\ No newline at end of file
......@@ -34,4 +34,4 @@ alter table ACT_RE_PROCDEF
add constraint ACT_UNIQ_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -19,4 +19,4 @@ alter table ACT_HI_TASKINST
add TENANT_ID_ NVARCHAR2(255) default '';
alter table ACT_HI_ACTINST
alter column ASSIGNEE_ NVARCHAR2(255);
\ No newline at end of file
modify ASSIGNEE_ NVARCHAR2(255);
\ No newline at end of file
......@@ -34,4 +34,4 @@ alter table ACT_RE_PROCDEF
add constraint ACT_UNIQ_PROCDEF
unique (KEY_,VERSION_, TENANT_ID_);
update ACT_GE_PROPERTY set VALUE_ = '5.15-SNAPSHOT' where NAME_ = 'schema.version';
update ACT_GE_PROPERTY set VALUE_ = '5.15' where NAME_ = 'schema.version';
......@@ -19,4 +19,4 @@ alter table ACT_HI_TASKINST
add TENANT_ID_ varchar(255) default '';
alter table ACT_HI_ACTINST
alter column ASSIGNEE_ varchar(255);
\ No newline at end of file
alter column ASSIGNEE_ TYPE varchar(255);
\ No newline at end of file
package org.activiti.standalone.calendar;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.activiti.engine.impl.calendar.AdvancedCycleBusinessCalendar;
import org.activiti.engine.impl.test.PvmTestCase;
import org.activiti.engine.impl.util.DefaultClockImpl;
import org.activiti.engine.runtime.Clock;
public class AdvancedCycleBusinessCalendarTest extends PvmTestCase {
private static final Clock testingClock = new DefaultClockImpl();
public void testDaylightSavingFallIso() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20131103-04:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20131104-05:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("R2/2013-11-03T00:00:00-04:00/P1D DSTZONE:US/Eastern"));
}
public void testDaylightSavingSpringIso() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140309-05:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20140310-04:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("R2/2014-03-09T00:00:00-05:00/P1D DSTZONE:US/Eastern"));
}
public void testIsoString() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140310-04:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20140311-04:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("R2/2014-03-10T04:00:00/P1D DSTZONE:US/Eastern"));
}
public void testLegacyIsoString() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140310-04:00:00", TimeZone.getDefault()));
assertEquals(parseCalendar("20140311-00:00:00", TimeZone.getDefault()).getTime(), businessCalendar.resolveDuedate("R2/2014-03-10T00:00:00/P1D"));
}
public void testDaylightSavingFallCron() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20131103-04:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20131103-17:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("0 0 12 1/1 * ? * DSTZONE:US/Eastern"));
}
public void testDaylightSavingSpringCron() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140309-05:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20140309-16:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("0 0 12 1/1 * ? * DSTZONE:US/Eastern"));
}
public void testCronString() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140310-04:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20140310-16:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("0 0 12 1/1 * ? * DSTZONE:US/Eastern"));
}
public void testLegacyCronString() throws Exception {
AdvancedCycleBusinessCalendar businessCalendar = new AdvancedCycleBusinessCalendar(testingClock);
testingClock.setCurrentCalendar(parseCalendar("20140310-04:00:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20140310-12:00:00", TimeZone.getTimeZone("UTC")).getTime(), businessCalendar.resolveDuedate("0 0 12 1/1 * ? *"));
}
private Calendar parseCalendar(String str, TimeZone timeZone) throws Exception {
return parseCalendar(str, timeZone, "yyyyMMdd-HH:mm:ss");
}
private Calendar parseCalendar(String str, TimeZone timeZone, String format) throws Exception {
Calendar date = new GregorianCalendar(timeZone);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
simpleDateFormat.setTimeZone(timeZone);
date.setTime(simpleDateFormat.parse(str));
return date;
}
}
......@@ -15,14 +15,15 @@
package org.activiti.standalone.calendar;
import org.activiti.engine.impl.calendar.DurationHelper;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.util.DefaultClockImpl;
import org.activiti.engine.runtime.Clock;
import org.junit.After;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import static groovy.util.GroovyTestCase.assertEquals;
import static org.junit.Assert.assertNull;
......@@ -71,11 +72,145 @@ public class DurationHelperTest {
assertEquals(parse("19700101-00:00:40"), dh.getDateAfter());
}
@Test
public void daylightSavingFall() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-04:45:00", TimeZone.getTimeZone("UTC")));
DurationHelper dh = new DurationHelper("R2/2013-11-03T00:45:00-04:00/PT1H", testingClock);
assertEquals(parseCalendar("20131103-05:45:00", TimeZone.getTimeZone("UTC")), dh.getCalendarAfter(testingClock.getCurrentCalendar(TimeZone.getTimeZone("US/Eastern"))));
testingClock.setCurrentCalendar(parseCalendar("20131103-05:45:00", TimeZone.getTimeZone("UTC")));
assertEquals(parseCalendar("20131103-06:45:00", TimeZone.getTimeZone("UTC")), dh.getCalendarAfter(testingClock.getCurrentCalendar(TimeZone.getTimeZone("US/Eastern"))));
}
@Test
public void daylightSavingFallFirstHour() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-05:45:00", TimeZone.getTimeZone("UTC")));
Calendar easternTime = testingClock.getCurrentCalendar(TimeZone.getTimeZone("US/Eastern"));
DurationHelper dh = new DurationHelper("R2/2013-11-03T01:45:00-04:00/PT1H", testingClock);
assertEquals(parseCalendar("20131103-06:45:00", TimeZone.getTimeZone("UTC")), dh.getCalendarAfter(easternTime));
}
@Test
public void daylightSavingFallSecondHour() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-06:45:00", TimeZone.getTimeZone("UTC")));
Calendar easternTime = testingClock.getCurrentCalendar(TimeZone.getTimeZone("US/Eastern"));
DurationHelper dh = new DurationHelper("R2/2013-11-03T01:45:00-05:00/PT1H", testingClock);
assertEquals(parseCalendar("20131103-07:45:00", TimeZone.getTimeZone("UTC")), dh.getCalendarAfter(easternTime));
}
@Test
public void daylightSavingFallObservedFirstHour() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-00:45:00", TimeZone.getTimeZone("US/Eastern")));
DurationHelper dh = new DurationHelper("R2/2013-11-03T00:45:00-04:00/PT1H", testingClock);
Calendar expected = parseCalendarWithOffset("20131103-01:45:00 -04:00", TimeZone.getTimeZone("US/Eastern"));
assertEquals(expected, dh.getCalendarAfter());
}
@Test
public void daylightSavingFallObservedSecondHour() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-00:45:00", TimeZone.getTimeZone("US/Eastern")));
DurationHelper dh = new DurationHelper("R2/2013-11-03T00:45:00-04:00/PT2H", testingClock);
Calendar expected = parseCalendarWithOffset("20131103-01:45:00 -05:00", TimeZone.getTimeZone("US/Eastern"));
assertEquals(expected, dh.getCalendarAfter());
}
@Test
public void daylightSavingSpring() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20140309-05:45:00", TimeZone.getTimeZone("UTC")));
DurationHelper dh = new DurationHelper("R2/2014-03-09T00:45:00-05:00/PT1H", testingClock);
assertEquals(parseCalendar("20140309-06:45:00", TimeZone.getTimeZone("UTC")), dh.getCalendarAfter(testingClock.getCurrentCalendar(TimeZone.getTimeZone("US/Eastern"))));
}
@Test
public void daylightSavingSpringObserved() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20140309-01:45:00", TimeZone.getTimeZone("US/Eastern")));
DurationHelper dh = new DurationHelper("R2/2014-03-09T01:45:00/PT1H", testingClock);
Calendar expected = parseCalendar("20140309-03:45:00", TimeZone.getTimeZone("US/Eastern"));
assertEquals(expected, dh.getCalendarAfter());
}
@Test
public void daylightSaving25HourDay() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131103-00:00:00", TimeZone.getTimeZone("US/Eastern")));
DurationHelper dh = new DurationHelper("R2/2013-11-03T00:00:00/P1D", testingClock);
assertEquals(parseCalendar("20131104-00:00:00", TimeZone.getTimeZone("US/Eastern")), dh.getCalendarAfter(testingClock.getCurrentCalendar()));
}
@Test
public void daylightSaving23HourDay() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20140309-00:00:00", TimeZone.getTimeZone("US/Eastern")));
DurationHelper dh = new DurationHelper("R2/2014-03-09T00:00:00/P1D", testingClock);
assertEquals(parseCalendar("20140310-00:00:00", TimeZone.getTimeZone("US/Eastern")), dh.getCalendarAfter(testingClock.getCurrentCalendar()));
}
@Test
public void daylightSaving25HourDayEurope() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20131027-00:00:00", TimeZone.getTimeZone("Europe/Amsterdam")));
DurationHelper dh = new DurationHelper("R2/2013-10-27T00:00:00/P1D", testingClock);
assertEquals(parseCalendar("20131028-00:00:00", TimeZone.getTimeZone("Europe/Amsterdam")), dh.getCalendarAfter(testingClock.getCurrentCalendar()));
}
@Test
public void daylightSaving23HourDayEurope() throws Exception {
Clock testingClock = new DefaultClockImpl();
testingClock.setCurrentCalendar(parseCalendar("20140330-00:00:00", TimeZone.getTimeZone("Europe/Amsterdam")));
DurationHelper dh = new DurationHelper("R2/2014-03-30T00:00:00/P1D", testingClock);
assertEquals(parseCalendar("20140331-00:00:00", TimeZone.getTimeZone("Europe/Amsterdam")), dh.getCalendarAfter(testingClock.getCurrentCalendar()));
}
private Date parse(String str) throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
return simpleDateFormat.parse(str);
}
private Calendar parseCalendarWithOffset(String str, TimeZone timeZone) throws Exception {
return parseCalendar(str, timeZone, "yyyyMMdd-HH:mm:ss X");
}
private Calendar parseCalendar(String str, TimeZone timeZone) throws Exception {
return parseCalendar(str, timeZone, "yyyyMMdd-HH:mm:ss");
}
private Calendar parseCalendar(String str, TimeZone timeZone, String format) throws Exception {
Calendar date = new GregorianCalendar(timeZone);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
simpleDateFormat.setTimeZone(timeZone);
date.setTime(simpleDateFormat.parse(str));
return date;
}
}
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<build>
......
......@@ -79,10 +79,10 @@ public class BlueprintBasicTest {
@Configuration
public Option[] createConfiguration() {
Option[] coreBundles = options(
mavenBundle().groupId("org.activiti").artifactId("activiti-bpmn-model").version("5.15-SNAPSHOT"),
mavenBundle().groupId("org.activiti").artifactId("activiti-bpmn-converter").version("5.15-SNAPSHOT"),
mavenBundle().groupId("org.activiti").artifactId("activiti-process-validation").version("5.15-SNAPSHOT"),
mavenBundle().groupId("org.activiti").artifactId("activiti-engine").version("5.15-SNAPSHOT"),
mavenBundle().groupId("org.activiti").artifactId("activiti-bpmn-model").version("5.15"),
mavenBundle().groupId("org.activiti").artifactId("activiti-bpmn-converter").version("5.15"),
mavenBundle().groupId("org.activiti").artifactId("activiti-process-validation").version("5.15"),
mavenBundle().groupId("org.activiti").artifactId("activiti-engine").version("5.15"),
mavenBundle().groupId("org.apache.commons").artifactId("commons-lang3").version("3.1"),
mavenBundle().groupId("org.codehaus.jackson").artifactId("jackson-core-asl").version("1.9.9"),
mavenBundle().groupId("log4j").artifactId("log4j").version("1.2.17"),
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<dependencies>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -9,7 +9,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
package org.activiti.spring;
import org.activiti.engine.impl.calendar.AdvancedCycleBusinessCalendar;
import org.activiti.engine.impl.calendar.DueDateBusinessCalendar;
import org.activiti.engine.impl.calendar.DurationBusinessCalendar;
import org.activiti.engine.impl.calendar.BusinessCalendarManager;
import org.activiti.engine.impl.calendar.MapBusinessCalendarManager;
import org.activiti.engine.impl.util.DefaultClockImpl;
import org.activiti.engine.runtime.Clock;
/**
* Creates an advanced cycle business calendar manager (ACBCM). The ACBCM can handle daylight
* savings changes when the scheduled time zone is different than the server time zone.
* <p>
* Create a factory bean
* <pre>
* &lt;bean id="businessCalendarManagerFactory" class="org.activiti.spring.SpringAdvancedBusinessCalendarManagerFactory" /&gt;
* </pre>
* Add the manager to your org.activiti.spring.SpringProcessEngineConfiguration bean
* <pre>
* &lt;bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"&gt;
* ...
* &lt;property name="businessCalendarManager"&gt;
* &lt;bean id="advancedBusinessCalendarManager" factory-bean="businessCalendarManagerFactory" factory-method="getBusinessCalendarManager" /&gt;
* &lt;/property&gt;
* ...
* &lt;/bean&gt;
* </pre>
*
* @see AdvancedCycleBusinessCalendar
* @author mseiden
*/
public class SpringAdvancedBusinessCalendarManagerFactory {
private Integer defaultScheduleVersion;
private Clock clock;
public Integer getDefaultScheduleVersion() {
return defaultScheduleVersion;
}
public void setDefaultScheduleVersion(Integer defaultScheduleVersion) {
this.defaultScheduleVersion = defaultScheduleVersion;
}
public Clock getClock() {
if (clock == null) {
clock = new DefaultClockImpl();
}
return clock;
}
public void setClock(Clock clock) {
this.clock = clock;
}
public BusinessCalendarManager getBusinessCalendarManager() {
MapBusinessCalendarManager mapBusinessCalendarManager = new MapBusinessCalendarManager();
mapBusinessCalendarManager.addBusinessCalendar(DurationBusinessCalendar.NAME, new DurationBusinessCalendar(getClock()));
mapBusinessCalendarManager.addBusinessCalendar(DueDateBusinessCalendar.NAME, new DueDateBusinessCalendar(getClock()));
mapBusinessCalendarManager.addBusinessCalendar(AdvancedCycleBusinessCalendar.NAME, new AdvancedCycleBusinessCalendar(getClock(), defaultScheduleVersion));
return mapBusinessCalendarManager;
}
}
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<build>
......
......@@ -11,7 +11,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<properties>
......
......@@ -10,7 +10,7 @@
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<build>
......
......@@ -14,7 +14,7 @@
</parent>
<packaging>pom</packaging>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.framework.version>3.2.7.RELEASE</spring.framework.version>
......
......@@ -4,7 +4,7 @@
<property file="${user.home}/.activiti/build.properties" />
<property file="../distro/src/setup/build.properties" />
<property name="activiti.version" value="5.15-SNAPSHOT" />
<property name="activiti.version" value="5.15" />
<property name="activiti.home" value="../distro/target/activiti-${activiti.version}" />
<property name="macos.browser" value="/usr/bin/open" />
<property name="windows.browser" value="C:/Program Files/Mozilla Firefox/firefox.exe" />
......
......@@ -8,7 +8,7 @@
<artifactId>activiti-dbclean</artifactId>
<packaging>pom</packaging>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
<dependencies>
<dependency>
......
......@@ -2,7 +2,7 @@
<project name="activiti.qa.upgrade" default="upgrade">
<property file="${user.home}/.activiti/build.properties" />
<property name="activiti.version" value="5.15-SNAPSHOT" />
<property name="activiti.version" value="5.15" />
<property name="database" value="h2" />
<property name="activiti.old.version" value="5.0" />
<property name="downloads.dir" value="${user.home}/.activiti/downloads" />
......
......@@ -6,13 +6,13 @@
<name>Activiti - Upgrade Fetcher</name>
<groupId>org.activiti</groupId>
<artifactId>activiti-upgrade-fetcher</artifactId>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
<parent>
<groupId>org.activiti</groupId>
<artifactId>activiti-root</artifactId>
<relativePath>../..</relativePath>
<version>5.15-SNAPSHOT</version>
<version>5.15</version>
</parent>
<dependencies>
......
......@@ -9876,8 +9876,8 @@ Only the attachment name is required to create a new attachment.
<programlisting>
{
"next.dbid":"101",
"schema.history":"create(5.15-SNAPSHOT)",
"schema.version":"5.15-SNAPSHOT"
"schema.history":"create(5.15)",
"schema.version":"5.15"
}</programlisting>
<table>
<title>Get engine properties - Response codes</title>
......@@ -9911,7 +9911,7 @@ Only the attachment name is required to create a new attachment.
<programlisting>
{
"name":"default",
"version":"5.15-SNAPSHOT",
"version":"5.15",
"resourceUrl":"file://activiti/activiti.cfg.xml",
"exception":null
}</programlisting>
......@@ -11586,7 +11586,7 @@ Only the attachment name is required to create a new attachment.
<section>
<title>Legacy REST - General Usage</title>
<para><emphasis role="bold">The following section contains documentation of the Legacy REST-api, which has been deprecated since the 5.15-SNAPSHOT release. The REST-urls will not be removed in the future but will not be maintained. Any future additions and improvements will be done to the new REST API.</emphasis></para>
<para><emphasis role="bold">The following section contains documentation of the Legacy REST-api, which has been deprecated since the 5.15 release. The REST-urls will not be removed in the future but will not be maintained. Any future additions and improvements will be done to the new REST API.</emphasis></para>
<para>
Activiti includes a REST API to the Activiti Engine that can be installed by deploying the activiti-rest.war file to a servlet container like Apache Tomcat.
By default the Activiti Engine will connect to a standalone H2 database. You can change the database settings in the db.properties file in the WEB-INF/classes folder.
......
......@@ -22,7 +22,7 @@
<book>
<bookinfo><title>Activiti 5.15-SNAPSHOT User Guide</title></bookinfo>
<bookinfo><title>Activiti 5.15 User Guide</title></bookinfo>
<toc />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册