diff --git a/README.md b/README.md
index a8027b0..4d35246 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+### Development in this repository is stopped. Future development continues on https://github.com/yigit/android-priority-jobqueue
+==========================
+

Android Priority Job Queue (Job Manager)
@@ -129,7 +132,7 @@ At Path, we use [greenrobot's EventBus](https://github.com/greenrobot/EventBus);
### Getting Started
We distribute artifacts through maven central repository.
-Gradle: `compile 'com.path:android-priority-jobqueue:1.0'`
+Gradle: `compile 'com.path:android-priority-jobqueue:1.1.2'`
Maven:
@@ -137,7 +140,7 @@ Maven:
com.path
android-priority-jobqueue
- 1.0
+ 1.1.2
```
@@ -150,7 +153,13 @@ We highly recommend checking how you can configure job manager and individual jo
* [Review sample configuration][7]
### Version History
-
+ - 1.1.2 (Feb 18, 2014)
+ - Report exceptions to logger if addInBackground fails. (#31)
+ - 1.1.1 (Feb 8, 2014)
+ - Fixed an important bug (#35) where jobs in the same group may run in parallel if many of them become available at the same time while multiple consumer threads are waiting for a new job.
+ - 1.1 (Jan 30, 2014)
+ - Job Status query API (#18)
+ - Fixed a stackoverflow bug when network status changes after a long time. (#21)
- 1.0 (Jan 14, 2014):
- Added [parameterized][12] constructor for Job for more readable code.
- Deprecated `BaseJob` in favor of a more complete `Job` class.
diff --git a/examples/twitter/TwitterClient/build.gradle b/examples/twitter/TwitterClient/build.gradle
index d5d7bad..cdc0a8d 100644
--- a/examples/twitter/TwitterClient/build.gradle
+++ b/examples/twitter/TwitterClient/build.gradle
@@ -1,10 +1,15 @@
+task wrapper(type: Wrapper) {
+ gradleVersion = '1.10'
+}
+
+
buildscript {
repositories {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.7.+'
+ classpath 'com.android.tools.build:gradle:0.8.+'
}
}
@@ -45,7 +50,7 @@ tasks.create(name: "runTwitter", type: RunApk){
android {
compileSdkVersion 17
- buildToolsVersion "18.1.0"
+ buildToolsVersion "19.0.1"
defaultConfig {
minSdkVersion 15
targetSdkVersion 17
@@ -53,7 +58,7 @@ android {
dependencies {
compile 'de.greenrobot:eventbus:2.1.0-beta-1'
- compile 'org.twitter4j:twitter4j-core:3.0.3'
+ compile 'org.twitter4j:twitter4j-core:3.0.5'
compile 'com.path:android-priority-jobqueue:1.1-SNAPSHOT'
compile files('external-libs/greenDAO.jar')
}
diff --git a/examples/twitter/TwitterClient/gradlew b/examples/twitter/TwitterClient/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/examples/twitter/TwitterClient/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/examples/twitter/TwitterClient/gradlew.bat b/examples/twitter/TwitterClient/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/examples/twitter/TwitterClient/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/Config.java b/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/Config.java
index 0d60b42..7c2e6a7 100644
--- a/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/Config.java
+++ b/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/Config.java
@@ -1,11 +1,11 @@
package com.path.android.jobqueue.examples.twitter;
public class Config {
- public static final String CONSUMER_KEY = "W1aPoCdw3QjdKpRm4NdTQ";
- public static final String CONSUMER_SECRET = "fCvy2QV2uSmaaPHRkYQqFA4dZHrusjvQR5lNmEeHI";
+ public static final String CONSUMER_KEY = "APEGHy66BMYzvgEktDfc1Q";
+ public static final String CONSUMER_SECRET = "wf8XXYwivxHQtiIqPSD3lpq6po9JGRyYBIX0lT0";
- public static final String ACCESS_TOKEN = "1443060589-h6JU83NsHMYx5M47Is2RzlVZmvHPbxQND9xT6KQ";
- public static final String ACCESS_TOKEN_SECRET = "QLut9Mgwge5WptlVnCz9wxmbJrqBFNazkEYrGDZKYE";
+ public static final String ACCESS_TOKEN = "1443060589-ZW0lPmcN0NOwy2AchLjZjixHLhPXClYOPQ0IhWG";
+ public static final String ACCESS_TOKEN_SECRET = "Tq8aVvT1PA6PXtKHgI5v1EL5UQj3JcGlFzXla2zethjYO";
public static final String REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token";
public static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";
diff --git a/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/controllers/TwitterController.java b/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/controllers/TwitterController.java
index 18e9200..abd4635 100644
--- a/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/controllers/TwitterController.java
+++ b/examples/twitter/TwitterClient/src/com/path/android/jobqueue/examples/twitter/controllers/TwitterController.java
@@ -3,6 +3,7 @@
import com.path.android.jobqueue.examples.twitter.Config;
import twitter4j.*;
import twitter4j.auth.AccessToken;
+import twitter4j.conf.ConfigurationBuilder;
import java.util.List;
@@ -21,7 +22,13 @@ public synchronized static TwitterController getInstance() {
}
public TwitterController() {
- twitter = TwitterFactory.getSingleton();
+ twitter = new TwitterFactory(new ConfigurationBuilder()
+ .setOAuthAccessToken(Config.ACCESS_TOKEN)
+ .setOAuthAccessTokenSecret(Config.ACCESS_TOKEN_SECRET)
+ .setDebugEnabled(true)
+ .setOAuthConsumerKey(Config.CONSUMER_KEY)
+ .setOAuthConsumerKey(Config.CONSUMER_SECRET)
+ .build()).getSingleton();
AccessToken accessToken = new AccessToken(Config.ACCESS_TOKEN, Config.ACCESS_TOKEN_SECRET);
twitter.setOAuthConsumer(Config.CONSUMER_KEY, Config.CONSUMER_SECRET);
twitter.setOAuthAccessToken(accessToken);
diff --git a/jobqueue/AndroidManifest.xml b/jobqueue/AndroidManifest.xml
index 604d7e6..7e37c4b 100644
--- a/jobqueue/AndroidManifest.xml
+++ b/jobqueue/AndroidManifest.xml
@@ -2,7 +2,7 @@
+ android:versionName="1.1.2">
diff --git a/jobqueue/build.gradle b/jobqueue/build.gradle
index 856bf65..e6d5fbf 100644
--- a/jobqueue/build.gradle
+++ b/jobqueue/build.gradle
@@ -1,4 +1,8 @@
import java.util.regex.Pattern
+task wrapper(type: Wrapper) {
+ gradleVersion = '1.10'
+}
+
apply plugin: 'maven'
apply plugin: 'signing'
@@ -8,7 +12,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.7.+'
+ classpath 'com.android.tools.build:gradle:0.8.+'
}
}
@@ -22,7 +26,7 @@ apply plugin: 'android-library'
android {
compileSdkVersion 19
- buildToolsVersion "18.1.0"
+ buildToolsVersion "19.0.1"
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
diff --git a/jobqueue/build.xml b/jobqueue/build.xml
index a11dd7e..279be48 100644
--- a/jobqueue/build.xml
+++ b/jobqueue/build.xml
@@ -193,7 +193,7 @@
-
+
diff --git a/jobqueue/gradle/wrapper/gradle-wrapper.jar b/jobqueue/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5838598
Binary files /dev/null and b/jobqueue/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/jobqueue/gradle/wrapper/gradle-wrapper.properties b/jobqueue/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..59739c3
--- /dev/null
+++ b/jobqueue/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 30 14:19:18 PST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
diff --git a/jobqueue/gradlew b/jobqueue/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/jobqueue/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/jobqueue/gradlew.bat b/jobqueue/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/jobqueue/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/jobqueue/src/com/path/android/jobqueue/JobManager.java b/jobqueue/src/com/path/android/jobqueue/JobManager.java
index 4c795a9..1ac6968 100644
--- a/jobqueue/src/com/path/android/jobqueue/JobManager.java
+++ b/jobqueue/src/com/path/android/jobqueue/JobManager.java
@@ -11,6 +11,7 @@
import com.path.android.jobqueue.nonPersistentQueue.NonPersistentPriorityQueue;
import com.path.android.jobqueue.persistentQueue.sqlite.SqliteJobQueue;
+import java.util.Collection;
import java.util.concurrent.*;
/**
@@ -40,6 +41,7 @@ public class JobManager implements NetworkEventProvider.Listener {
private final ConcurrentHashMap persistentOnAddedLocks;
private final ConcurrentHashMap nonPersistentOnAddedLocks;
private final ScheduledExecutorService timedExecutor;
+ private final Object getNextJobLock = new Object();
/**
* Default constructor that will create a JobManager with 1 {@link SqliteJobQueue} and 1 {@link NonPersistentPriorityQueue}
@@ -260,30 +262,36 @@ private JobHolder getNextJob() {
boolean haveNetwork = hasNetwork();
JobHolder jobHolder;
boolean persistent = false;
- synchronized (nonPersistentJobQueue) {
- jobHolder = nonPersistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobGroups.getSafe());
- }
- if (jobHolder == null) {
- //go to disk, there aren't any non-persistent jobs
- synchronized (persistentJobQueue) {
- jobHolder = persistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobGroups.getSafe());
- persistent = true;
+ synchronized (getNextJobLock) {
+ final Collection runningJobIds = runningJobGroups.getSafe();
+ synchronized (nonPersistentJobQueue) {
+ jobHolder = nonPersistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobIds);
}
- }
- if(jobHolder != null) {
- //wait for onAdded locks
- if(persistent) {
- waitForOnAddedLock(persistentOnAddedLocks, jobHolder.getId());
- } else {
- waitForOnAddedLock(nonPersistentOnAddedLocks, jobHolder.getId());
+ if (jobHolder == null) {
+ //go to disk, there aren't any non-persistent jobs
+ synchronized (persistentJobQueue) {
+ jobHolder = persistentJobQueue.nextJobAndIncRunCount(haveNetwork, runningJobIds);
+ persistent = true;
+ }
+ }
+ if(jobHolder == null) {
+ return null;
+ }
+ if(persistent && dependencyInjector != null) {
+ dependencyInjector.inject(jobHolder.getBaseJob());
+ }
+ if(jobHolder.getGroupId() != null) {
+ runningJobGroups.add(jobHolder.getGroupId());
}
}
- if(persistent && jobHolder != null && dependencyInjector != null) {
- dependencyInjector.inject(jobHolder.getBaseJob());
- }
- if(jobHolder != null && jobHolder.getGroupId() != null) {
- runningJobGroups.add(jobHolder.getGroupId());
+
+ //wait for onAdded locks. wait for locks after job is selected so that we minimize the lock
+ if(persistent) {
+ waitForOnAddedLock(persistentOnAddedLocks, jobHolder.getId());
+ } else {
+ waitForOnAddedLock(nonPersistentOnAddedLocks, jobHolder.getId());
}
+
return jobHolder;
}
@@ -552,10 +560,14 @@ protected void addJobInBackground(final int priority, final long delay, final Ba
timedExecutor.execute(new Runnable() {
@Override
public void run() {
- final long runDelay = (System.nanoTime() - callTime) / NS_PER_MS;
- long id = addJob(priority, Math.max(0, delay - runDelay), baseJob);
- if(callback != null) {
- callback.onAdded(id);
+ try {
+ final long runDelay = (System.nanoTime() - callTime) / NS_PER_MS;
+ long id = addJob(priority, Math.max(0, delay - runDelay), baseJob);
+ if(callback != null) {
+ callback.onAdded(id);
+ }
+ } catch (Throwable t) {
+ JqLog.e(t, "addJobInBackground received an exception. job class: %s", baseJob.getClass().getSimpleName() );
}
}
});
diff --git a/jobqueue/src/com/path/android/jobqueue/Params.java b/jobqueue/src/com/path/android/jobqueue/Params.java
index c3e323a..1695d94 100644
--- a/jobqueue/src/com/path/android/jobqueue/Params.java
+++ b/jobqueue/src/com/path/android/jobqueue/Params.java
@@ -97,23 +97,23 @@ public Params setDelayMs(long delayMs) {
return this;
}
- boolean doesRequireNetwork() {
+ public boolean doesRequireNetwork() {
return requiresNetwork;
}
- String getGroupId() {
+ public String getGroupId() {
return groupId;
}
- boolean isPersistent() {
+ public boolean isPersistent() {
return persistent;
}
- int getPriority() {
+ public int getPriority() {
return priority;
}
- long getDelayMs() {
+ public long getDelayMs() {
return delayMs;
}
}
diff --git a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java
index 00103e9..0739d3c 100644
--- a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java
+++ b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/GroupingTest.java
@@ -3,6 +3,7 @@
import com.path.android.jobqueue.JobHolder;
import com.path.android.jobqueue.JobManager;
import com.path.android.jobqueue.Params;
+import com.path.android.jobqueue.config.Configuration;
import com.path.android.jobqueue.test.jobs.DummyJob;
import org.fest.reflect.method.*;
import static org.hamcrest.CoreMatchers.*;
@@ -11,6 +12,9 @@
import org.junit.runner.RunWith;
import org.robolectric.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
@RunWith(RobolectricTestRunner.class)
public class GroupingTest extends JobManagerTestBase {
@Test
@@ -35,4 +39,68 @@ public void testGrouping() throws Exception {
removeJobMethod.invoke(group2Job);
MatcherAssert.assertThat("even after group2 job is complete, no jobs should be returned since we only have group1 jobs left", nextJobMethod.invoke(), is(nullValue()));
}
+
+ @Test
+ public void testGroupingRaceCondition() throws Exception {
+ DummyNetworkUtilWithConnectivityEventSupport dummyNetworkUtil = new DummyNetworkUtilWithConnectivityEventSupport();
+ JobManager jobManager = createJobManager(new Configuration.Builder(Robolectric.application)
+ .minConsumerCount(5).maxConsumerCount(10)
+ .networkUtil(dummyNetworkUtil));
+ dummyNetworkUtil.setHasNetwork(false, true);
+ //add a bunch of network requring jobs
+ final String GROUP_ID = "shared_group_id";
+ final int AFTER_ADDED_JOBS_COUNT = 5;
+ final int NOT_SET_JOB_ID = -1;
+ final AtomicInteger firstRunJob = new AtomicInteger(NOT_SET_JOB_ID);
+ final int FIRST_JOB_ID = -10;
+ final CountDownLatch onAddedCalled = new CountDownLatch(1);
+ final CountDownLatch remainingJobsOnAddedCalled = new CountDownLatch(AFTER_ADDED_JOBS_COUNT);
+ jobManager.addJobInBackground(new DummyJob(new Params(10).requireNetwork().groupBy(GROUP_ID)) {
+ @Override
+ public void onAdded() {
+ super.onAdded();
+ onAddedCalled.countDown();
+ try {
+ //wait until all other jobs are added
+ remainingJobsOnAddedCalled.await();
+ //wait a bit after all are added,
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ @Override
+ public void onRun() throws Throwable {
+ super.onRun();
+ firstRunJob.compareAndSet(NOT_SET_JOB_ID, FIRST_JOB_ID);
+ }
+ });
+ //ensure first jobs on added is called
+ onAddedCalled.await();
+ for(int i = 0; i < AFTER_ADDED_JOBS_COUNT; i ++) {
+ final int finalI = i;
+ jobManager.addJob(new DummyJob(new Params(5).groupBy(GROUP_ID).requireNetwork()) {
+ final int id = finalI + 1;
+
+ @Override
+ public void onAdded() {
+ super.onAdded();
+ remainingJobsOnAddedCalled.countDown();
+ }
+
+ @Override
+ public void onRun() throws Throwable {
+ super.onRun();
+ firstRunJob.compareAndSet(NOT_SET_JOB_ID, id);
+ }
+ });
+ }
+ dummyNetworkUtil.setHasNetwork(true, true);
+ //wait until all jobs are completed
+ while(firstRunJob.get() == NOT_SET_JOB_ID) {
+ Thread.sleep(100);
+ }
+ MatcherAssert.assertThat("highest priority job should run if it is added before others", firstRunJob.get(), is(FIRST_JOB_ID));
+
+ }
}
diff --git a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/InjectorTest.java b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/InjectorTest.java
index ac6152f..7129710 100644
--- a/jobqueue/test/com/path/android/jobqueue/test/jobmanager/InjectorTest.java
+++ b/jobqueue/test/com/path/android/jobqueue/test/jobmanager/InjectorTest.java
@@ -6,6 +6,7 @@
import com.path.android.jobqueue.Params;
import com.path.android.jobqueue.config.Configuration;
import com.path.android.jobqueue.di.DependencyInjector;
+import com.path.android.jobqueue.log.CustomLogger;
import com.path.android.jobqueue.test.jobs.DummyJob;
import static org.hamcrest.CoreMatchers.*;
import org.hamcrest.*;
@@ -13,6 +14,8 @@
import org.junit.runner.RunWith;
import org.robolectric.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@RunWith(RobolectricTestRunner.class)
@@ -42,6 +45,55 @@ public void inject(BaseJob job) {
holder = getNextJobMethod(jobManager).invoke();
MatcherAssert.assertThat("injection should be called for persistent job", holder.getBaseJob(), equalTo(injectedJobReference.getObject()));
MatcherAssert.assertThat("injection should be called two times for persistent job", injectionCallCount.get(), equalTo(3));
+ }
+
+ @Test
+ public void testInjectorCrash() throws Exception {
+ final String EXCEPTION_MESSAGE = "could not inject for whatever reason :)";
+ DependencyInjector dummyDependencyInjector = new DependencyInjector() {
+ @Override
+ public void inject(BaseJob baseJob) {
+ throw new RuntimeException(EXCEPTION_MESSAGE);
+ }
+ };
+
+ final ObjectReference objectReference = new ObjectReference();
+ final CountDownLatch exceptionLatch = new CountDownLatch(1);
+ CustomLogger customLogger = new CustomLogger() {
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ @Override
+ public void d(String s, Object... objects) {
+
+ }
+
+ @Override
+ public void e(Throwable throwable, String s, Object... objects) {
+ objectReference.setObject(throwable);
+ exceptionLatch.countDown();
+ }
+
+ @Override
+ public void e(String s, Object... objects) {
+ //
+ }
+ };
+ JobManager jobManager = createJobManager(new Configuration.Builder(Robolectric.application).injector(dummyDependencyInjector).customLogger(customLogger));
+ Throwable addException = null;
+ try {
+ jobManager.addJob(new DummyJob(new Params(0)));
+ } catch (Throwable t) {
+ addException = t;
+ }
+ MatcherAssert.assertThat("addJob should throw exception if dependency injector throws exception", addException, notNullValue());
+ jobManager.addJobInBackground(new DummyJob(new Params(0)));
+ exceptionLatch.await(2, TimeUnit.SECONDS);
+ MatcherAssert.assertThat("there should be a received exception", objectReference.getObject(), notNullValue());
+ MatcherAssert.assertThat("logged exception should be a runtime exception", objectReference.getObject(), instanceOf(RuntimeException.class));
+ MatcherAssert.assertThat("logged exception should have expected message", ((Throwable)objectReference.getObject()).getMessage(), is(EXCEPTION_MESSAGE));
}
}