From 7ee1347ab43bd505680c3018cac19e63b5ddcdb2 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 9 Sep 2019 21:29:07 +0800 Subject: [PATCH 001/140] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppFrame/build.gradle | 2 +- Aria/build.gradle | 7 +- .../aria/core/command/GroupCmdFactory.java | 4 +- .../aria/core/download/CheckDEntityUtil.java | 14 +-- .../aria/core/download/DTaskWrapper.java | 17 ++++ .../aria/core/download/DownloadReceiver.java | 60 +++++------- .../download/group/GroupTargetFactory.java | 65 ------------- .../AbsGroupConfigHandler.java | 6 +- .../DNormalConfigHandler.java | 11 +-- .../DTargetFactory.java} | 41 +++++--- .../{normal => target}/FtpBuilderTarget.java | 6 +- .../FtpDirBuilderTarget.java | 5 +- .../FtpDirConfigHandler.java | 3 +- .../{group => target}/FtpDirNormalTarget.java | 5 +- .../{normal => target}/FtpNormalTarget.java | 6 +- .../{group => target}/GroupBuilderTarget.java | 5 +- .../{group => target}/GroupNormalTarget.java | 5 +- .../{normal => target}/HttpBuilderTarget.java | 6 +- .../HttpGroupConfigHandler.java | 3 +- .../{normal => target}/HttpNormalTarget.java | 6 +- .../download/target/TcpBuilderTarget.java | 73 ++++++++++++++ .../aria/core/download/tcp/TcpDelegate.java | 97 +++++++++++++++++++ .../aria/core/download/tcp/TcpTaskConfig.java | 77 +++++++++++++++ .../com/arialyy/aria/core/inf/AbsTarget.java | 14 +-- .../arialyy/aria/core/inf/ITaskWrapper.java | 18 ++++ .../aria/core/manager/SubTaskManager.java | 14 +-- .../aria/core/upload/CheckUEntityUtil.java | 15 +-- .../aria/core/upload/UploadReceiver.java | 38 +++----- .../{normal => target}/FtpBuilderTarget.java | 6 +- .../{normal => target}/FtpNormalTarget.java | 6 +- .../{normal => target}/HttpBuilderTarget.java | 6 +- .../{normal => target}/HttpNormalTarget.java | 6 +- .../UNormalConfigHandler.java | 9 +- .../UTargetFactory.java} | 28 +++--- .../com/arialyy/aria/util/CommonUtil.java | 8 +- AriaFtpComponent/.gitignore | 1 + AriaFtpComponent/build.gradle | 32 ++++++ AriaFtpComponent/consumer-rules.pro | 0 AriaFtpComponent/proguard-rules.pro | 21 ++++ .../ExampleInstrumentedTest.java | 26 +++++ AriaFtpComponent/src/main/AndroidManifest.xml | 2 + .../com/arialyy/aria/ftpcomponent/bb.java | 4 + .../src/main/res/values/strings.xml | 3 + .../ariaftpcomponent/ExampleUnitTest.java | 17 ++++ .../assets/help_code/KotlinHttpDownload.kt | 2 +- build.gradle | 7 +- settings.gradle | 2 +- 47 files changed, 540 insertions(+), 269 deletions(-) delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/group/GroupTargetFactory.java rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/AbsGroupConfigHandler.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal => target}/DNormalConfigHandler.java (91%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal/DNormalTargetFactory.java => target/DTargetFactory.java} (56%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal => target}/FtpBuilderTarget.java (94%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/FtpDirBuilderTarget.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/FtpDirConfigHandler.java (92%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/FtpDirNormalTarget.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal => target}/FtpNormalTarget.java (93%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/GroupBuilderTarget.java (97%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/GroupNormalTarget.java (96%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal => target}/HttpBuilderTarget.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/download/{group => target}/HttpGroupConfigHandler.java (97%) rename Aria/src/main/java/com/arialyy/aria/core/download/{normal => target}/HttpNormalTarget.java (93%) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpTaskConfig.java rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal => target}/FtpBuilderTarget.java (92%) rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal => target}/FtpNormalTarget.java (91%) rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal => target}/HttpBuilderTarget.java (90%) rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal => target}/HttpNormalTarget.java (91%) rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal => target}/UNormalConfigHandler.java (92%) rename Aria/src/main/java/com/arialyy/aria/core/upload/{normal/UNormalTargetFactory.java => target/UTargetFactory.java} (66%) create mode 100644 AriaFtpComponent/.gitignore create mode 100644 AriaFtpComponent/build.gradle create mode 100644 AriaFtpComponent/consumer-rules.pro create mode 100644 AriaFtpComponent/proguard-rules.pro create mode 100644 AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java create mode 100644 AriaFtpComponent/src/main/AndroidManifest.xml create mode 100644 AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java create mode 100644 AriaFtpComponent/src/main/res/values/strings.xml create mode 100644 AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java diff --git a/AppFrame/build.gradle b/AppFrame/build.gradle index 7ff09de7..bf09497a 100644 --- a/AppFrame/build.gradle +++ b/AppFrame/build.gradle @@ -29,7 +29,7 @@ dependencies { exclude group: 'com.android.support', module: 'support-annotations' }) testImplementation 'junit:junit:4.12' - api 'androidx.appcompat:appcompat:1.0.2' + api "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" api 'com.google.code.gson:gson:2.8.2' api 'io.reactivex:rxandroid:1.2.0' api 'io.reactivex:rxjava:1.1.5' diff --git a/Aria/build.gradle b/Aria/build.gradle index ca7841a2..39b76600 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -24,10 +24,11 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation 'junit:junit:4.12' - implementation 'androidx.appcompat:appcompat:1.0.0' + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" api project(':AriaAnnotations') - api 'com.arialyy.aria:aria-ftp-plug:1.0.4' // 打包时用这个 + api 'com.arialyy.aria:aria-ftp-plug:1.0.4' // 打包时用这个 + + // compile project(':AriaFtpPlug') -// compile project(':AriaFtpPlug') } apply from: 'bintray-release.gradle' diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java index db7b1f31..0edbfec9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java @@ -46,13 +46,11 @@ public class GroupCmdFactory { } /** - * @param target 创建任务的对象 * @param wrapper 参数信息 * @param type 命令类型{@link #SUB_TASK_START}、{@link #SUB_TASK_STOP} * @param childUrl 需要控制的子任务url */ - public AbsGroupCmd createCmd(String target, AbsGroupTaskWrapper wrapper, int type, - String childUrl) { + public AbsGroupCmd createCmd(AbsGroupTaskWrapper wrapper, int type, String childUrl) { AbsGroupCmd cmd = null; switch (type) { case SUB_TASK_START: diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index ea81c0e1..360cec28 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -51,7 +51,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { return false; } - boolean b = checkFtps() && checkUrl() && checkFilePath(); + boolean b = checkUrl() && checkFilePath(); if (b) { mEntity.save(); } @@ -188,16 +188,4 @@ public class CheckDEntityUtil implements ICheckEntityUtil { } return true; } - - private boolean checkFtps() { - //if (mWrapper.getRequestType() == ITaskWrapper.D_FTP && mWrapper.asFtp() - // .getUrlEntity().isFtps) { - // String ftpUrl = mEntity.getUrl(); - // if (!ftpUrl.startsWith("ftps") && !ftpUrl.startsWith("sftp")) { - // ALog.e(TAG, String.format("地址【%s】错误,ftps地址开头必须是:ftps或sftp", ftpUrl)); - // return false; - // } - //} - return true; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java index f86fcb91..8ee29ab7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.download.m3u8.M3U8TaskConfig; +import com.arialyy.aria.core.download.tcp.TcpTaskConfig; import com.arialyy.aria.core.inf.AbsTaskWrapper; /** @@ -35,12 +36,21 @@ public class DTaskWrapper extends AbsTaskWrapper { */ private boolean isGroupTask = false; + /** + * M3u8任务配置信息 + */ private M3U8TaskConfig m3u8TaskConfig; + /** + * TCP任务配置信息 + */ + private TcpTaskConfig tcpTaskConfig; + /** * 文件下载url的临时保存变量 */ private String mTempUrl; + /** * 文件保存路径的临时变量 */ @@ -62,6 +72,13 @@ public class DTaskWrapper extends AbsTaskWrapper { return m3u8TaskConfig; } + public TcpTaskConfig asTcp() { + if (tcpTaskConfig == null) { + tcpTaskConfig = new TcpTaskConfig(); + } + return tcpTaskConfig; + } + /** * Task实体对应的key,下载url */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 3c28d429..71e5d906 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.core.download; -import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; @@ -24,16 +23,15 @@ import com.arialyy.aria.core.command.CancelAllCmd; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.ProxyHelper; -import com.arialyy.aria.core.download.group.FtpDirBuilderTarget; -import com.arialyy.aria.core.download.group.FtpDirNormalTarget; -import com.arialyy.aria.core.download.group.GroupBuilderTarget; -import com.arialyy.aria.core.download.group.GroupNormalTarget; -import com.arialyy.aria.core.download.group.GroupTargetFactory; -import com.arialyy.aria.core.download.normal.FtpBuilderTarget; -import com.arialyy.aria.core.download.normal.FtpNormalTarget; -import com.arialyy.aria.core.download.normal.HttpBuilderTarget; -import com.arialyy.aria.core.download.normal.HttpNormalTarget; -import com.arialyy.aria.core.download.normal.DNormalTargetFactory; +import com.arialyy.aria.core.download.target.DTargetFactory; +import com.arialyy.aria.core.download.target.FtpBuilderTarget; +import com.arialyy.aria.core.download.target.FtpDirBuilderTarget; +import com.arialyy.aria.core.download.target.FtpDirNormalTarget; +import com.arialyy.aria.core.download.target.FtpNormalTarget; +import com.arialyy.aria.core.download.target.GroupBuilderTarget; +import com.arialyy.aria.core.download.target.GroupNormalTarget; +import com.arialyy.aria.core.download.target.HttpBuilderTarget; +import com.arialyy.aria.core.download.target.HttpNormalTarget; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; @@ -76,8 +74,8 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public HttpBuilderTarget load(@NonNull String url) { CheckUtil.checkUrlInvalidThrow(url); - return DNormalTargetFactory.getInstance() - .generateBuilderTarget(HttpBuilderTarget.class, url, targetName); + return DTargetFactory.getInstance() + .generateBuilderTarget(HttpBuilderTarget.class, url); } /** @@ -89,8 +87,8 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public HttpNormalTarget load(long taskId) { CheckUtil.checkTaskId(taskId); - return DNormalTargetFactory.getInstance() - .generateNormalTarget(HttpNormalTarget.class, taskId, targetName); + return DTargetFactory.getInstance() + .generateNormalTarget(HttpNormalTarget.class, taskId); } /** @@ -101,7 +99,7 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public GroupBuilderTarget loadGroup(List urls) { CheckUtil.checkDownloadUrls(urls); - return GroupTargetFactory.getInstance().generateGroupBuilderTarget(urls, targetName); + return DTargetFactory.getInstance().generateGroupBuilderTarget(urls); } /** @@ -113,8 +111,8 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public GroupNormalTarget loadGroup(long taskId) { CheckUtil.checkTaskId(taskId); - return GroupTargetFactory.getInstance() - .generateNormalTarget(GroupNormalTarget.class, taskId, targetName); + return DTargetFactory.getInstance() + .generateNormalTarget(GroupNormalTarget.class, taskId); } /** @@ -123,8 +121,8 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpBuilderTarget loadFtp(@NonNull String url) { CheckUtil.checkUrlInvalidThrow(url); - return DNormalTargetFactory.getInstance() - .generateBuilderTarget(FtpBuilderTarget.class, url, targetName); + return DTargetFactory.getInstance() + .generateBuilderTarget(FtpBuilderTarget.class, url); } /** @@ -136,8 +134,8 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpNormalTarget loadFtp(long taskId) { CheckUtil.checkTaskId(taskId); - return DNormalTargetFactory.getInstance() - .generateNormalTarget(FtpNormalTarget.class, taskId, targetName); + return DTargetFactory.getInstance() + .generateNormalTarget(FtpNormalTarget.class, taskId); } /** @@ -146,7 +144,7 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpDirBuilderTarget loadFtpDir(@NonNull String dirUrl) { CheckUtil.checkUrlInvalidThrow(dirUrl); - return GroupTargetFactory.getInstance().generateDirBuilderTarget(dirUrl, targetName); + return DTargetFactory.getInstance().generateDirBuilderTarget(dirUrl); } /** @@ -158,21 +156,17 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpDirNormalTarget loadFtpDir(long taskId) { CheckUtil.checkTaskId(taskId); - return GroupTargetFactory.getInstance() - .generateNormalTarget(FtpDirNormalTarget.class, taskId, targetName); + return DTargetFactory.getInstance() + .generateNormalTarget(FtpDirNormalTarget.class, taskId); } /** * 将当前类注册到Aria */ public void register() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "download register target null"); - return; - } Object obj = OBJ_MAP.get(getKey()); if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); return; } Set set = ProxyHelper.getInstance().checkProxyType(obj.getClass()); @@ -216,13 +210,9 @@ public class DownloadReceiver extends AbsReceiver { } @Override protected void unRegisterListener() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "download unRegisterListener target null"); - return; - } Object obj = OBJ_MAP.get(getKey()); if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); return; } Set set = ProxyHelper.getInstance().mProxyCache.get(obj.getClass().getName()); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupTargetFactory.java b/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupTargetFactory.java deleted file mode 100644 index 73ab3772..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupTargetFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.download.group; - -import com.arialyy.aria.core.common.AbsNormalTarget; -import java.util.List; - -/** - * @Author aria - * @Date 2019-09-05 - */ -public class GroupTargetFactory { - - public static volatile GroupTargetFactory INSTANCE; - - private GroupTargetFactory() { - - } - - public static GroupTargetFactory getInstance() { - - if (INSTANCE == null) { - synchronized (GroupTargetFactory.class) { - if (INSTANCE == null) { - INSTANCE = new GroupTargetFactory(); - } - } - } - - return INSTANCE; - } - - public T generateNormalTarget(Class clazz, long taskId, - String targetName) { - T target = null; - if (clazz == GroupNormalTarget.class) { - target = (T) new GroupNormalTarget(taskId, targetName); - } else if (clazz == FtpDirNormalTarget.class) { - target = (T) new FtpDirNormalTarget(taskId, targetName); - } - - return target; - } - - public FtpDirBuilderTarget generateDirBuilderTarget(String url, String targetName) { - return new FtpDirBuilderTarget(url, targetName); - } - - public GroupBuilderTarget generateGroupBuilderTarget(List urls, String targetName) { - return new GroupBuilderTarget(urls, targetName); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupConfigHandler.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java index 30a26e2f..d3eae6e8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import android.text.TextUtils; +import androidx.annotation.CheckResult; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTask; @@ -61,7 +61,7 @@ abstract class AbsGroupConfigHandler implements IConfi @CheckResult SubTaskManager getSubTaskManager() { if (mSubTaskManager == null) { - mSubTaskManager = new SubTaskManager(mTarget.getTargetName(), getTaskWrapper()); + mSubTaskManager = new SubTaskManager(getTaskWrapper()); } return mSubTaskManager; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalConfigHandler.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java index 2c86fc08..5eeb8aea 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import android.text.TextUtils; import com.arialyy.aria.core.download.DTaskWrapper; @@ -40,26 +40,25 @@ class DNormalConfigHandler implements IConfigHandler { /** * @param taskId 第一次下载,taskId为-1 */ - DNormalConfigHandler(TARGET target, long taskId, String targetName) { + DNormalConfigHandler(TARGET target, long taskId) { this.mTarget = target; - initTarget(taskId, targetName); + initTarget(taskId); } - private void initTarget(long taskId, String targetName) { + private void initTarget(long taskId) { mWrapper = TaskWrapperManager.getInstance().getNormalTaskWrapper(DTaskWrapper.class, taskId); if (taskId != -1 && mWrapper.getEntity().getId() == -1) { mWrapper.setErrorEvent(new ErrorEvent(taskId, String.format("没有id为%s的任务", taskId))); } mEntity = mWrapper.getEntity(); - mTarget.setTargetName(targetName); mTarget.setTaskWrapper(mWrapper); if (mEntity != null) { getWrapper().setTempFilePath(mEntity.getFilePath()); } } - public TARGET updateUrl(String newUrl) { + TARGET updateUrl(String newUrl) { if (TextUtils.isEmpty(newUrl)) { ALog.e(TAG, "url更新失败,newUrl为null"); return mTarget; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalTargetFactory.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/DTargetFactory.java similarity index 56% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalTargetFactory.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/DTargetFactory.java index 31655cb3..693430e4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/DNormalTargetFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/DTargetFactory.java @@ -13,29 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.AbsNormalTarget; +import java.util.List; /** * @Author aria * @Date 2019-09-05 */ -public class DNormalTargetFactory { +public class DTargetFactory { - public static volatile DNormalTargetFactory INSTANCE; + public static volatile DTargetFactory INSTANCE; - private DNormalTargetFactory() { + private DTargetFactory() { } - public static DNormalTargetFactory getInstance() { + public static DTargetFactory getInstance() { if (INSTANCE == null) { - synchronized (DNormalTargetFactory.class) { + synchronized (DTargetFactory.class) { if (INSTANCE == null) { - INSTANCE = new DNormalTargetFactory(); + INSTANCE = new DTargetFactory(); } } } @@ -43,27 +44,37 @@ public class DNormalTargetFactory { return INSTANCE; } - public T generateNormalTarget(Class clazz, long taskId, - String targetName) { + public T generateNormalTarget(Class clazz, long taskId) { T target = null; if (clazz == HttpNormalTarget.class) { - target = (T) new HttpNormalTarget(taskId, targetName); + target = (T) new HttpNormalTarget(taskId); } else if (clazz == FtpNormalTarget.class) { - target = (T) new FtpNormalTarget(taskId, targetName); + target = (T) new FtpNormalTarget(taskId); + } else if (clazz == GroupNormalTarget.class) { + target = (T) new GroupNormalTarget(taskId); + } else if (clazz == FtpDirNormalTarget.class) { + target = (T) new FtpDirNormalTarget(taskId); } return target; } - public T generateBuilderTarget(Class clazz, String url, - String targetName) { + public T generateBuilderTarget(Class clazz, String url) { T target = null; if (clazz == HttpBuilderTarget.class) { - target = (T) new HttpBuilderTarget(url, targetName); + target = (T) new HttpBuilderTarget(url); } else if (clazz == FtpBuilderTarget.class) { - target = (T) new FtpBuilderTarget(url, targetName); + target = (T) new FtpBuilderTarget(url); } return target; } + + public FtpDirBuilderTarget generateDirBuilderTarget(String url) { + return new FtpDirBuilderTarget(url); + } + + public GroupBuilderTarget generateGroupBuilderTarget(List urls) { + return new GroupBuilderTarget(urls); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java similarity index 94% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 6f904f73..55c44716 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; @@ -30,8 +30,8 @@ import com.arialyy.aria.util.CommonUtil; public class FtpBuilderTarget extends AbsBuilderTarget { private DNormalConfigHandler mConfigHandler; - FtpBuilderTarget(String url, String targetName) { - mConfigHandler = new DNormalConfigHandler<>(this, -1, targetName); + FtpBuilderTarget(String url) { + mConfigHandler = new DNormalConfigHandler<>(this, -1); mConfigHandler.setUrl(url); getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(url)); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index c581a363..a2e97691 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; @@ -30,8 +30,7 @@ import com.arialyy.aria.util.CommonUtil; public class FtpDirBuilderTarget extends AbsBuilderTarget { private FtpDirConfigHandler mConfigHandler; - FtpDirBuilderTarget(String url, String targetName) { - setTargetName(targetName); + FtpDirBuilderTarget(String url) { mConfigHandler = new FtpDirConfigHandler<>(this, -1); getEntity().setGroupHash(url); getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(url)); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirConfigHandler.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java index ef75325d..35720be0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.target.AbsGroupConfigHandler; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.AbsTaskWrapper; import com.arialyy.aria.core.inf.ITaskWrapper; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index 45bd433b..742f455f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -30,8 +30,7 @@ import com.arialyy.aria.util.CommonUtil; public class FtpDirNormalTarget extends AbsNormalTarget { private FtpDirConfigHandler mConfigHandler; - FtpDirNormalTarget(long taskId, String targetName) { - setTargetName(targetName); + FtpDirNormalTarget(long taskId) { mConfigHandler = new FtpDirConfigHandler<>(this, taskId); getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java similarity index 93% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index 10aa07c8..8c912f06 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; @@ -31,8 +31,8 @@ import com.arialyy.aria.util.CommonUtil; public class FtpNormalTarget extends AbsNormalTarget { private DNormalConfigHandler mConfigHandler; - FtpNormalTarget(long taskId, String targetName) { - mConfigHandler = new DNormalConfigHandler<>(this, taskId, targetName); + FtpNormalTarget(long taskId) { + mConfigHandler = new DNormalConfigHandler<>(this, taskId); getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/GroupBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index c9dd1724..813561d8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; @@ -33,8 +33,7 @@ import java.util.List; public class GroupBuilderTarget extends AbsBuilderTarget { private HttpGroupConfigHandler mConfigHandler; - GroupBuilderTarget(List urls, String targetName) { - setTargetName(targetName); + GroupBuilderTarget(List urls) { mConfigHandler = new HttpGroupConfigHandler<>(this, -1); getTaskWrapper().setRequestType(ITaskWrapper.DG_HTTP); mConfigHandler.setGroupUrl(urls); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/GroupNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 4ba0cbed..2e7855b1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -30,8 +30,7 @@ import java.util.List; public class GroupNormalTarget extends AbsNormalTarget { private HttpGroupConfigHandler mConfigHandler; - GroupNormalTarget(long taskId, String targetName) { - setTargetName(targetName); + GroupNormalTarget(long taskId) { mConfigHandler = new HttpGroupConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(ITaskWrapper.DG_HTTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index 7f68a5a7..e5eb6d5b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; @@ -28,8 +28,8 @@ public class HttpBuilderTarget extends AbsBuilderTarget { private DNormalConfigHandler mConfigHandler; - HttpBuilderTarget(String url, String targetName) { - mConfigHandler = new DNormalConfigHandler<>(this, -1, targetName); + HttpBuilderTarget(String url) { + mConfigHandler = new DNormalConfigHandler<>(this, -1); getTaskWrapper().setRequestType(ITaskWrapper.D_HTTP); mConfigHandler.setUrl(url); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/HttpGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/HttpGroupConfigHandler.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java index b5a3c914..5ac49fb1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/HttpGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.target.AbsGroupConfigHandler; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java similarity index 93% rename from Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java index 36cf7421..ada14aff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/normal/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.normal; +package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -29,8 +29,8 @@ import com.arialyy.aria.core.download.m3u8.M3U8Delegate; public class HttpNormalTarget extends AbsNormalTarget { private DNormalConfigHandler mConfigHandler; - HttpNormalTarget(long taskId, String targetName) { - mConfigHandler = new DNormalConfigHandler<>(this, taskId, targetName); + HttpNormalTarget(long taskId) { + mConfigHandler = new DNormalConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(getTaskWrapper().getEntity().getTaskType()); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java new file mode 100644 index 00000000..64b6041a --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.target; + +import androidx.annotation.CheckResult; +import androidx.annotation.NonNull; +import com.arialyy.aria.core.common.AbsBuilderTarget; +import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.download.tcp.TcpDelegate; +import com.arialyy.aria.core.inf.ITaskWrapper; + +/** + * @Author aria + * @Date 2019-09-06 + */ +public class TcpBuilderTarget extends AbsBuilderTarget { + + private DNormalConfigHandler mConfigHandler; + + TcpBuilderTarget(String ip, int port) { + mConfigHandler = new DNormalConfigHandler<>(this, -1); + getTaskWrapper().setRequestType(ITaskWrapper.D_TCP); + } + + /** + * 设置tcp相应信息 + */ + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public TcpDelegate option() { + return new TcpDelegate<>(this, getTaskWrapper()); + } + + /** + * 设置文件存储路径,如果需要修改新的文件名,修改路径便可。 + * 如:原文件路径 /mnt/sdcard/test.zip + * 如果需要将test.zip改为game.zip,只需要重新设置文件路径为:/mnt/sdcard/game.zip + * + * @param filePath 路径必须为文件路径,不能为文件夹路径 + */ + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public TcpBuilderTarget setFilePath(@NonNull String filePath) { + mConfigHandler.setTempFilePath(filePath); + return this; + } + + /** + * 设置文件存储路径,如果需要修改新的文件名,修改路径便可。 + * 如:原文件路径 /mnt/sdcard/test.zip + * 如果需要将test.zip改为game.zip,只需要重新设置文件路径为:/mnt/sdcard/game.zip + * + * @param filePath 路径必须为文件路径,不能为文件夹路径 + * @param forceDownload {@code true}强制下载,不考虑文件路径是否被占用 + */ + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public TcpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { + mConfigHandler.setTempFilePath(filePath); + mConfigHandler.setForceDownload(forceDownload); + return this; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java new file mode 100644 index 00000000..b8d395a5 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.tcp; + +import android.text.TextUtils; +import androidx.annotation.CheckResult; +import com.arialyy.aria.core.common.BaseDelegate; +import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.util.ALog; +import java.nio.charset.Charset; + +/** + * @Author aria + * @Date 2019-09-06 + */ +public class TcpDelegate extends BaseDelegate { + + private TcpTaskConfig mTcpConfig; + + public TcpDelegate(TARGET target, AbsTaskWrapper wrapper) { + super(target, wrapper); + + mTcpConfig = ((DTaskWrapper) wrapper).asTcp(); + } + + /** + * 上传给tcp服务的初始数据,一般是文件名、文件路径等信息 + */ + @CheckResult(suggest = Suggest.TO_CONTROLLER) + public TcpDelegate setParam(String params) { + if (TextUtils.isEmpty(params)) { + ALog.w(TAG, "tcp传输的数据不能为空"); + return this; + } + mTcpConfig.setParams(params); + return this; + } + + /** + * 设置心跳包传输的数据 + * + * @param heartbeatInfo 心跳包数据 + */ + @CheckResult(suggest = Suggest.TO_CONTROLLER) + public TcpDelegate setHeartbeatInfo(String heartbeatInfo) { + if (TextUtils.isEmpty(heartbeatInfo)) { + ALog.w(TAG, "心跳包传输的数据不能为空"); + return this; + } + mTcpConfig.setHeartbeat(heartbeatInfo); + return this; + } + + /** + * 心跳间隔,默认1s + * + * @param interval 单位毫秒 + */ + @CheckResult(suggest = Suggest.TO_CONTROLLER) + public TcpDelegate setHeartbeatInterval(long interval) { + if (interval <= 0) { + ALog.w(TAG, "心跳间隔不能小于1毫秒"); + return this; + } + mTcpConfig.setHeartbeatInterval(interval); + return this; + } + + /** + * 数据传输编码,默认"utf-8" + */ + public TcpDelegate setCharset(String charset) { + if (!Charset.isSupported(charset)) { + ALog.w(TAG, "不支持的编码"); + return this; + } + + mTcpConfig.setCharset(charset); + return this; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpTaskConfig.java b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpTaskConfig.java new file mode 100644 index 00000000..58fee361 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpTaskConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.tcp; + +/** + * tcp任务配置 + * + * @Author aria + * @Date 2019-09-06 + */ +public class TcpTaskConfig { + + /** + * 上传给tcp服务的初始数据,一般是文件名等信息 + */ + private String params; + + /** + * 心跳包传输的数据 + */ + private String heartbeat; + + /** + * 心跳间隔,单位毫秒,默认1s + */ + private long heartbeatInterval = 1000; + + /** + * 数据传输编码,默认"utf-8" + */ + private String charset = "utf-8"; + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getHeartbeat() { + return heartbeat; + } + + public void setHeartbeat(String heartbeat) { + this.heartbeat = heartbeat; + } + + public long getHeartbeatInterval() { + return heartbeatInterval; + } + + public void setHeartbeatInterval(long heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 806a39fe..3c190bf0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -18,8 +18,8 @@ package com.arialyy.aria.core.inf; import android.text.TextUtils; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.controller.NormalController; import com.arialyy.aria.core.common.controller.BuilderController; +import com.arialyy.aria.core.common.controller.NormalController; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -31,7 +31,6 @@ public abstract class AbsTarget { protected String TAG; private AbsEntity mEntity; private AbsTaskWrapper mTaskWrapper; - private String mTargetName; protected AbsTarget() { TAG = CommonUtil.getClassName(this); @@ -46,14 +45,6 @@ public abstract class AbsTarget { return mEntity; } - public String getTargetName() { - return mTargetName; - } - - public void setTargetName(String mTargetName) { - this.mTargetName = mTargetName; - } - /** * 获取任务实体 */ @@ -84,7 +75,8 @@ public abstract class AbsTarget { /** * 重置状态,将任务状态设置为未开始状态 * 注意:如果在后续方法调用链中没有调用 {@link NormalController#stop()}、{@link NormalController#cancel()}、 - * {@link NormalController#resume()}、{@link BuilderController#create()}、{@link BuilderController#add()} + * {@link NormalController#resume()}、{@link BuilderController#create()}、{@link + * BuilderController#add()} * 等操作任务的方法,那么你需要调用{@link NormalController#save()}才能将修改保存到数据库 */ @CheckResult(suggest = "after use #create()、#stop()、#cancel()、#resume()、#save()?") diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java index 701247fa..192be8b5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java @@ -28,6 +28,7 @@ public interface ITaskWrapper { * HTTP单任务载 */ int D_HTTP = 1; + /** * HTTP任务组下载 */ @@ -37,6 +38,7 @@ public interface ITaskWrapper { * FTP单文件下载 */ int D_FTP = 3; + /** * FTP文件夹下载,为避免登录过多,子任务由单线程进行处理 */ @@ -46,6 +48,7 @@ public interface ITaskWrapper { * HTTP单文件上传 */ int U_HTTP = 5; + /** * FTP单文件上传 */ @@ -61,5 +64,20 @@ public interface ITaskWrapper { */ int M3U8_LIVE = 8; + /** + * TCP 文件下载 + */ + int D_TCP = 9; + + /** + * TCP 文件上传 + */ + int U_TCP = 10; + + /** + * TCP 分片上传 + */ + int U_TCP_PEER = 11; + ENTITY getEntity(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java index b2f8126c..fbbfd03e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java @@ -30,10 +30,8 @@ import java.util.List; public class SubTaskManager { private String TAG = "SubTaskManager"; private DGTaskWrapper mEntity; - private String mTargetName; - public SubTaskManager(String targetName, DGTaskWrapper entity) { - mTargetName = targetName; + public SubTaskManager(DGTaskWrapper entity) { mEntity = entity; } @@ -44,9 +42,8 @@ public class SubTaskManager { */ public void startSubTask(String url) { if (checkUrl(url)) { - EventMsgUtil.getDefault() - .post( - CommonUtil.createGroupCmd(mTargetName, mEntity, GroupCmdFactory.SUB_TASK_START, url)); + EventMsgUtil.getDefault().post( + CommonUtil.createGroupCmd(mEntity, GroupCmdFactory.SUB_TASK_START, url)); } } @@ -57,9 +54,8 @@ public class SubTaskManager { */ public void stopSubTask(String url) { if (checkUrl(url)) { - EventMsgUtil.getDefault() - .post( - CommonUtil.createGroupCmd(mTargetName, mEntity, GroupCmdFactory.SUB_TASK_STOP, url)); + EventMsgUtil.getDefault().post( + CommonUtil.createGroupCmd(mEntity, GroupCmdFactory.SUB_TASK_STOP, url)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java index b92446bb..923a1fe1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java @@ -17,7 +17,6 @@ package com.arialyy.aria.core.upload; import android.text.TextUtils; import com.arialyy.aria.core.inf.ICheckEntityUtil; -import com.arialyy.aria.core.inf.ITaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import java.io.File; @@ -43,7 +42,7 @@ public class CheckUEntityUtil implements ICheckEntityUtil { return false; } - boolean b = checkFtps() && checkUrl() && checkFilePath(); + boolean b = checkUrl() && checkFilePath(); if (b) { mEntity.save(); } @@ -90,16 +89,4 @@ public class CheckUEntityUtil implements ICheckEntityUtil { mEntity.setUrl(url); return true; } - - private boolean checkFtps() { - //if (mWrapper.getRequestType() == ITaskWrapper.U_FTP && mWrapper.asFtp() - // .getUrlEntity().isFtps) { - // String ftpUrl = mEntity.getUrl(); - // if (!ftpUrl.startsWith("ftps") && !ftpUrl.startsWith("sftp")) { - // ALog.e(TAG, String.format("地址【%s】错误,ftps地址开头必须是:ftps或sftp", ftpUrl)); - // return false; - // } - //} - return true; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 99383582..0424fa47 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -30,11 +30,11 @@ import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.ITask; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.scheduler.TaskSchedulers; -import com.arialyy.aria.core.upload.normal.FtpBuilderTarget; -import com.arialyy.aria.core.upload.normal.FtpNormalTarget; -import com.arialyy.aria.core.upload.normal.HttpBuilderTarget; -import com.arialyy.aria.core.upload.normal.HttpNormalTarget; -import com.arialyy.aria.core.upload.normal.UNormalTargetFactory; +import com.arialyy.aria.core.upload.target.FtpBuilderTarget; +import com.arialyy.aria.core.upload.target.FtpNormalTarget; +import com.arialyy.aria.core.upload.target.HttpBuilderTarget; +import com.arialyy.aria.core.upload.target.HttpNormalTarget; +import com.arialyy.aria.core.upload.target.UTargetFactory; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; @@ -69,8 +69,8 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public HttpBuilderTarget load(@NonNull String filePath) { CheckUtil.checkUploadPath(filePath); - return UNormalTargetFactory.getInstance() - .generateBuilderTarget(HttpBuilderTarget.class, filePath, targetName); + return UTargetFactory.getInstance() + .generateBuilderTarget(HttpBuilderTarget.class, filePath); } /** @@ -82,8 +82,8 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public HttpNormalTarget load(long taskId) { CheckUtil.checkTaskId(taskId); - return UNormalTargetFactory.getInstance() - .generateNormalTarget(HttpNormalTarget.class, taskId, targetName); + return UTargetFactory.getInstance() + .generateNormalTarget(HttpNormalTarget.class, taskId); } /** @@ -94,8 +94,8 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public FtpBuilderTarget loadFtp(@NonNull String filePath) { CheckUtil.checkUploadPath(filePath); - return UNormalTargetFactory.getInstance() - .generateBuilderTarget(FtpBuilderTarget.class, filePath, targetName); + return UTargetFactory.getInstance() + .generateBuilderTarget(FtpBuilderTarget.class, filePath); } /** @@ -107,8 +107,8 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public FtpNormalTarget loadFtp(long taskId) { CheckUtil.checkTaskId(taskId); - return UNormalTargetFactory.getInstance() - .generateNormalTarget(FtpNormalTarget.class, taskId, targetName); + return UTargetFactory.getInstance() + .generateNormalTarget(FtpNormalTarget.class, taskId); } /** @@ -252,13 +252,9 @@ public class UploadReceiver extends AbsReceiver { * 将当前类注册到Aria */ public void register() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "upload register target null"); - return; - } Object obj = OBJ_MAP.get(getKey()); if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); return; } Set set = ProxyHelper.getInstance().checkProxyType(obj.getClass()); @@ -289,13 +285,9 @@ public class UploadReceiver extends AbsReceiver { } @Override protected void unRegisterListener() { - if (TextUtils.isEmpty(targetName)) { - ALog.e(TAG, "upload unRegisterListener target null"); - return; - } Object obj = OBJ_MAP.get(getKey()); if (obj == null) { - ALog.e(TAG, String.format("【%s】观察者为空", targetName)); + ALog.e(TAG, String.format("【%s】观察者为空", getTargetName())); return; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index a823b4da..a23b04a2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; @@ -30,8 +30,8 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper; public class FtpBuilderTarget extends AbsBuilderTarget { private UNormalConfigHandler mConfigHandler; - FtpBuilderTarget(String filePath, String targetName) { - mConfigHandler = new UNormalConfigHandler<>(this, -1, targetName); + FtpBuilderTarget(String filePath) { + mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index ba6e8128..e2db2cc4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -30,8 +30,8 @@ import com.arialyy.aria.util.CommonUtil; public class FtpNormalTarget extends AbsNormalTarget { private UNormalConfigHandler mConfigHandler; - FtpNormalTarget(long taskId, String targetName) { - mConfigHandler = new UNormalConfigHandler<>(this, taskId, targetName); + FtpNormalTarget(long taskId) { + mConfigHandler = new UNormalConfigHandler<>(this, taskId); getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpBuilderTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 1badcbaa..53682c4e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; @@ -28,9 +28,9 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper; public class HttpBuilderTarget extends AbsBuilderTarget { private UNormalConfigHandler mConfigHandler; - HttpBuilderTarget(String filePath, String targetName) { + HttpBuilderTarget(String filePath) { - mConfigHandler = new UNormalConfigHandler<>(this, -1, targetName); + mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); //http暂时不支持断点上传 getTaskWrapper().setSupportBP(false); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpNormalTarget.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index c83e8b46..95bc0c94 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -28,8 +28,8 @@ import com.arialyy.aria.core.inf.AbsTaskWrapper; public class HttpNormalTarget extends AbsNormalTarget { private UNormalConfigHandler mConfigHandler; - HttpNormalTarget(long taskId, String targetName) { - mConfigHandler = new UNormalConfigHandler<>(this, taskId, targetName); + HttpNormalTarget(long taskId) { + mConfigHandler = new UNormalConfigHandler<>(this, taskId); getTaskWrapper().setSupportBP(false); getTaskWrapper().setRequestType(AbsTaskWrapper.U_HTTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalConfigHandler.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java index 90144aa9..9ccf2faa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; import com.arialyy.aria.core.event.ErrorEvent; @@ -39,18 +39,17 @@ class UNormalConfigHandler implements IConfigHandler { private TARGET mTarget; private UTaskWrapper mWrapper; - UNormalConfigHandler(TARGET target, long taskId, String targetName) { + UNormalConfigHandler(TARGET target, long taskId) { mTarget = target; - initTarget(taskId, targetName); + initTarget(taskId); } - private void initTarget(long taskId, String targetName) { + private void initTarget(long taskId) { mWrapper = TaskWrapperManager.getInstance().getNormalTaskWrapper(UTaskWrapper.class, taskId); if (taskId != -1 && mWrapper.getEntity().getId() == -1) { mWrapper.setErrorEvent(new ErrorEvent(taskId, String.format("没有id为%s的任务", taskId))); } mEntity = mWrapper.getEntity(); - mTarget.setTargetName(targetName); mTarget.setTaskWrapper(mWrapper); getTaskWrapper().setTempUrl(mEntity.getUrl()); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalTargetFactory.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UTargetFactory.java similarity index 66% rename from Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalTargetFactory.java rename to Aria/src/main/java/com/arialyy/aria/core/upload/target/UTargetFactory.java index bdd87e7a..42d794c7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/normal/UNormalTargetFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UTargetFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.normal; +package com.arialyy.aria.core.upload.target; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.AbsNormalTarget; @@ -22,20 +22,20 @@ import com.arialyy.aria.core.common.AbsNormalTarget; * @Author aria * @Date 2019-09-05 */ -public class UNormalTargetFactory { +public class UTargetFactory { - public static volatile UNormalTargetFactory INSTANCE; + public static volatile UTargetFactory INSTANCE; - private UNormalTargetFactory() { + private UTargetFactory() { } - public static UNormalTargetFactory getInstance() { + public static UTargetFactory getInstance() { if (INSTANCE == null) { - synchronized (UNormalTargetFactory.class) { + synchronized (UTargetFactory.class) { if (INSTANCE == null) { - INSTANCE = new UNormalTargetFactory(); + INSTANCE = new UTargetFactory(); } } } @@ -43,25 +43,23 @@ public class UNormalTargetFactory { return INSTANCE; } - public T generateNormalTarget(Class clazz, long taskId, - String targetName) { + public T generateNormalTarget(Class clazz, long taskId) { T target = null; if (clazz == HttpNormalTarget.class) { - target = (T) new HttpNormalTarget(taskId, targetName); + target = (T) new HttpNormalTarget(taskId); } else if (clazz == FtpNormalTarget.class) { - target = (T) new FtpNormalTarget(taskId, targetName); + target = (T) new FtpNormalTarget(taskId); } return target; } - public T generateBuilderTarget(Class clazz, String url, - String targetName) { + public T generateBuilderTarget(Class clazz, String url) { T target = null; if (clazz == HttpBuilderTarget.class) { - target = (T) new HttpBuilderTarget(url, targetName); + target = (T) new HttpBuilderTarget(url); } else if (clazz == FtpBuilderTarget.class) { - target = (T) new FtpBuilderTarget(url, targetName); + target = (T) new FtpBuilderTarget(url); } return target; diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index 2030159f..1d0871c3 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -24,11 +24,11 @@ import android.os.Environment; import android.text.TextUtils; import android.util.Base64; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.ftp.FtpUrlEntity; import com.arialyy.aria.core.command.AbsGroupCmd; import com.arialyy.aria.core.command.AbsNormalCmd; import com.arialyy.aria.core.command.GroupCmdFactory; import com.arialyy.aria.core.command.NormalCmdFactory; +import com.arialyy.aria.core.common.ftp.FtpUrlEntity; import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; import com.arialyy.aria.core.inf.AbsTaskWrapper; import com.arialyy.aria.core.inf.ITask; @@ -558,9 +558,9 @@ public class CommonUtil { * * @param childUrl 子任务url */ - public static AbsGroupCmd createGroupCmd(String target, T entity, - int cmd, String childUrl) { - return GroupCmdFactory.getInstance().createCmd(target, entity, cmd, childUrl); + public static AbsGroupCmd createGroupCmd(T entity, int cmd, + String childUrl) { + return GroupCmdFactory.getInstance().createCmd(entity, cmd, childUrl); } /** diff --git a/AriaFtpComponent/.gitignore b/AriaFtpComponent/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/AriaFtpComponent/.gitignore @@ -0,0 +1 @@ +/build diff --git a/AriaFtpComponent/build.gradle b/AriaFtpComponent/build.gradle new file mode 100644 index 00000000..12924738 --- /dev/null +++ b/AriaFtpComponent/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" + + testImplementation 'junit:junit:4.12' + implementation project(path: ':AriaFtpPlug') +} diff --git a/AriaFtpComponent/consumer-rules.pro b/AriaFtpComponent/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/AriaFtpComponent/proguard-rules.pro b/AriaFtpComponent/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/AriaFtpComponent/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java b/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java new file mode 100644 index 00000000..9e18a5cd --- /dev/null +++ b/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.ariaftpcomponent; + +import android.content.Context; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.example.ariaftpcomponent.test", appContext.getPackageName()); + } +} diff --git a/AriaFtpComponent/src/main/AndroidManifest.xml b/AriaFtpComponent/src/main/AndroidManifest.xml new file mode 100644 index 00000000..25aac7fe --- /dev/null +++ b/AriaFtpComponent/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java b/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java new file mode 100644 index 00000000..84ba97e3 --- /dev/null +++ b/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java @@ -0,0 +1,4 @@ +package com.arialyy.aria.ftpcomponent; + +public class bb { +} diff --git a/AriaFtpComponent/src/main/res/values/strings.xml b/AriaFtpComponent/src/main/res/values/strings.xml new file mode 100644 index 00000000..08862ffd --- /dev/null +++ b/AriaFtpComponent/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + AriaFtpComponent + diff --git a/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java b/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java new file mode 100644 index 00000000..79cfa215 --- /dev/null +++ b/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.ariaftpcomponent; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/app/src/main/assets/help_code/KotlinHttpDownload.kt b/app/src/main/assets/help_code/KotlinHttpDownload.kt index 9fc3bfcf..7f3033b9 100644 --- a/app/src/main/assets/help_code/KotlinHttpDownload.kt +++ b/app/src/main/assets/help_code/KotlinHttpDownload.kt @@ -21,7 +21,7 @@ import android.widget.Button import android.widget.Toast import com.arialyy.annotations.Download import com.arialyy.aria.core.Aria -import com.arialyy.aria.core.download.normal.HttpNormalTarget +import com.arialyy.aria.core.download.target.HttpNormalTarget import com.arialyy.aria.core.download.DownloadTask import com.arialyy.aria.core.inf.IEntity import com.arialyy.simple.R diff --git a/build.gradle b/build.gradle index 4bf2e476..40ad5c26 100644 --- a/build.gradle +++ b/build.gradle @@ -55,9 +55,8 @@ ext { buildToolsVersion = "28.0.3" targetSdkVersion = 28 lifecycleVersion = "1.1.1" -// compileSdkVersion = 28 -// supportLibVersion = "28.0.0" -// buildToolsVersion = "28.0.3" -// targetSdkVersion = 28 minSdkVersion = 15 + + // 以下是androidX + XAppcompatVersion = "1.1.0" } diff --git a/settings.gradle b/settings.gradle index 1ab002b2..b6d6bae2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug', ':AppFrame' +include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug', ':AppFrame', ':AriaFtpComponent' From 2a9232178528d006c1a67501df4e4ae872b2f63f Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sun, 29 Sep 2019 20:07:21 +0800 Subject: [PATCH 002/140] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8C=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/arialyy/frame/cache/DiskLruCache.java | 1 - Aria/build.gradle | 10 +- .../com/arialyy/aria/core/AriaManager.java | 139 +----- .../com/arialyy/aria/core/command/AbsCmd.java | 2 +- .../aria/core/command/AbsCmdFactory.java | 2 +- .../aria/core/command/AbsGroupCmd.java | 10 +- .../aria/core/command/AbsNormalCmd.java | 20 +- .../com/arialyy/aria/core/command/AddCmd.java | 4 +- .../aria/core/command/CancelAllCmd.java | 14 +- .../arialyy/aria/core/command/CancelCmd.java | 4 +- .../arialyy/aria/core/command/CmdHelper.java | 44 ++ .../aria/core/command/DGSubStartCmd.java | 2 +- .../aria/core/command/DGSubStopCmd.java | 2 +- .../aria/core/command/GroupCmdFactory.java | 2 +- .../aria/core/command/HighestPriorityCmd.java | 8 +- .../aria/core/command/NormalCmdFactory.java | 2 +- .../arialyy/aria/core/command/ReStartCmd.java | 4 +- .../aria/core/command/ResumeAllCmd.java | 18 +- .../arialyy/aria/core/command/StartCmd.java | 41 +- .../arialyy/aria/core/command/StopAllCmd.java | 2 +- .../arialyy/aria/core/command/StopCmd.java | 4 +- .../aria/core/common/AbsNormalTarget.java | 1 - .../aria/core/common/BaseDelegate.java | 3 +- .../core/common/{ftp => }/FTPSDelegate.java | 31 +- .../core/common/{ftp => }/FtpDelegate.java | 21 +- .../core/common/{http => }/HttpDelegate.java | 94 +--- .../aria/core/common/RecordHandler.java | 457 ------------------ .../common/controller/BuilderController.java | 10 +- .../common/controller/FeatureController.java | 10 +- .../common/controller/NormalController.java | 18 +- .../aria/core/download/CheckDEntityUtil.java | 28 +- .../aria/core/download/CheckDGEntityUtil.java | 8 +- .../core/download/CheckFtpDirEntityUtil.java | 11 +- .../aria/core/download/DownloadReceiver.java | 16 +- .../core/download/downloader/Downloader.java | 129 ----- .../downloader/SimpleDownloadUtil.java | 140 ------ .../core/download/group/SubDLoadUtil.java | 133 ----- .../aria/core/download/m3u8/M3U8Delegate.java | 22 +- .../core/download/m3u8/M3U8LiveDelegate.java | 12 +- .../core/download/m3u8/M3U8VodDelegate.java | 13 +- .../aria/core/download/m3u8/M3U8VodUtil.java | 145 ------ .../target/AbsGroupConfigHandler.java | 6 +- .../download/target/DNormalConfigHandler.java | 4 +- .../download/target/FtpBuilderTarget.java | 10 +- .../download/target/FtpDirBuilderTarget.java | 8 +- .../download/target/FtpDirConfigHandler.java | 5 +- .../download/target/FtpDirNormalTarget.java | 8 +- .../core/download/target/FtpNormalTarget.java | 10 +- .../download/target/GroupBuilderTarget.java | 11 +- .../download/target/GroupNormalTarget.java | 6 +- .../download/target/HttpBuilderTarget.java | 14 +- .../target/HttpGroupConfigHandler.java | 1 - .../download/target/HttpNormalTarget.java | 4 +- .../download/target/TcpBuilderTarget.java | 4 +- .../aria/core/download/tcp/TcpDelegate.java | 7 +- .../arialyy/aria/core/inf/AbsNormalTask.java | 36 -- .../arialyy/aria/core/inf/AbsReceiver.java | 8 +- .../com/arialyy/aria/core/inf/AbsTarget.java | 3 +- .../arialyy/aria/core/inf/IConfigHandler.java | 2 + .../core/manager/DTaskWrapperFactory.java | 6 +- .../core/manager/IGroupWrapperFactory.java | 4 +- .../aria/core/manager/INormalTEFactory.java | 4 +- .../aria/core/manager/SubTaskManager.java | 6 +- .../aria/core/manager/TaskWrapperManager.java | 2 +- .../arialyy/aria/core/queue/AbsTaskQueue.java | 18 +- ...oupTaskQueue.java => DGroupTaskQueue.java} | 23 +- ...DownloadTaskQueue.java => DTaskQueue.java} | 23 +- .../arialyy/aria/core/queue/ITaskQueue.java | 11 +- .../arialyy/aria/core/queue/TaskFactory.java | 12 +- .../{UploadTaskQueue.java => UTaskQueue.java} | 23 +- .../aria/core/queue/pool/BaseCachePool.java | 2 +- .../aria/core/queue/pool/BaseExecutePool.java | 2 +- .../core/queue/pool/DGLoadExecutePool.java | 2 +- .../core/queue/pool/DLoadExecutePool.java | 2 +- .../arialyy/aria/core/queue/pool/IPool.java | 4 +- .../core/queue/pool/UploadExecutePool.java | 2 +- .../core/scheduler/FailureTaskHandler.java | 4 +- .../core/scheduler/NormalTaskListener.java | 8 +- .../aria/core/scheduler/SubTaskListener.java | 4 +- .../aria/core/scheduler/TaskSchedulers.java | 29 +- .../aria/core/upload/UploadReceiver.java | 13 +- .../core/upload/target/FtpBuilderTarget.java | 8 +- .../core/upload/target/FtpNormalTarget.java | 11 +- .../core/upload/target/HttpBuilderTarget.java | 6 +- .../core/upload/target/HttpNormalTarget.java | 6 +- .../upload/target/UNormalConfigHandler.java | 17 +- .../upload/uploader/SimpleUploadUtil.java | 116 ----- .../aria/core/upload/uploader/Uploader.java | 61 --- .../com/arialyy/annotations/TaskEnum.java | 10 +- .../java/com/arialyy/compiler/EntityInfo.java | 2 +- .../ExampleInstrumentedTest.java | 26 - .../com/arialyy/aria/ftpcomponent/bb.java | 4 - .../ariaftpcomponent/ExampleUnitTest.java | 17 - {AriaFtpComponent => FtpComponent}/.gitignore | 0 FtpComponent/build.gradle | 33 ++ .../consumer-rules.pro | 0 .../proguard-rules.pro | 0 .../src/main/AndroidManifest.xml | 0 .../arialyy/aria}/ftp/AbsFtpInfoThread.java | 46 +- .../aria/ftp/BaseFtpThreadTaskAdapter.java | 35 +- .../arialyy/aria/ftp}/FtpDirInfoThread.java | 26 +- .../arialyy/aria/ftp/FtpRecordAdapter.java | 109 +++++ .../com/arialyy/aria/ftp/FtpTaskOption.java | 11 +- .../aria}/ftp/SSLSessionReuseFTPSClient.java | 2 +- .../aria/ftp/download/FtpDFileInfoThread.java | 12 +- .../aria/ftp/download/FtpDLoaderAdapter.java | 73 +++ .../aria/ftp/download/FtpDLoaderUtil.java | 59 +++ .../ftp/download/FtpDThreadTaskAdapter.java | 99 ++-- .../aria/ftp/download/FtpDirDLoaderUtil.java | 48 +- .../aria/ftp/download/SubDLoaderUtil.java | 49 ++ .../aria/ftp/upload}/FtpFISAdapter.java | 2 +- .../aria/ftp/upload/FtpUFileInfoThread.java | 26 +- .../aria/ftp/upload/FtpULoaderUtil.java | 62 +++ .../aria/ftp/upload/FtpULoaferAdapter.java | 56 +++ .../ftp/upload/FtpUThreadTaskAdapter.java | 87 ++-- .../src/main/res/values/strings.xml | 0 HttpComponent/.gitignore | 1 + HttpComponent/build.gradle | 32 ++ HttpComponent/consumer-rules.pro | 0 HttpComponent/proguard-rules.pro | 21 + HttpComponent/src/main/AndroidManifest.xml | 2 + .../aria/http/BaseHttpThreadTaskAdapter.java | 41 ++ .../aria/http}/ChunkedInputStream.java | 2 +- .../arialyy/aria/http}/ConnectionHelp.java | 19 +- .../aria/http}/HttpFileInfoThread.java | 42 +- .../arialyy/aria/http/HttpRecordAdapter.java | 109 +++++ .../com/arialyy/aria/http/HttpTaskOption.java | 8 +- .../aria/http/download/DGroupLoaderUtil.java | 77 +-- .../http/download/HttpDLoaderAdapter.java | 100 ++++ .../aria/http/download/HttpDLoaderUtil.java | 61 +++ .../http/download/HttpDThreadTaskAdapter.java | 117 ++--- .../aria/http/download/SubDLoaderUtil.java | 69 +++ .../aria/http/upload/HttpULoaderAdapter.java | 55 +++ .../aria/http/upload/HttpULoaderUtil.java | 49 ++ .../http/upload/HttpUThreadTaskAdapter.java | 74 ++- HttpComponent/src/main/res/values/strings.xml | 3 + M3U8Component/.gitignore | 1 + M3U8Component/build.gradle | 31 ++ M3U8Component/consumer-rules.pro | 0 M3U8Component/proguard-rules.pro | 21 + M3U8Component/src/main/AndroidManifest.xml | 2 + .../arialyy/aria}/m3u8/BaseM3U8Loader.java | 34 +- .../com/arialyy/aria/m3u8}/IdGenerator.java | 2 +- .../arialyy/aria}/m3u8/M3U8InfoThread.java | 68 +-- .../com/arialyy/aria/m3u8}/M3U8Listener.java | 14 +- .../arialyy/aria/m3u8/M3U8RecordAdapter.java | 129 +++++ .../com/arialyy/aria/m3u8/M3U8TaskOption.java | 19 +- .../aria/m3u8/M3U8ThreadTaskAdapter.java | 105 ++-- .../aria/m3u8/live}/M3U8LiveLoader.java | 42 +- .../arialyy/aria/m3u8/live}/M3U8LiveUtil.java | 154 +++--- .../arialyy/aria/m3u8/vod}/M3U8VodLoader.java | 82 ++-- .../arialyy/aria/m3u8/vod/M3U8VodUtil.java | 101 ++++ M3U8Component/src/main/res/values/strings.xml | 3 + PublicComponent/.gitignore | 1 + .../build.gradle | 7 +- PublicComponent/consumer-rules.pro | 0 PublicComponent/proguard-rules.pro | 21 + PublicComponent/src/main/AndroidManifest.xml | 5 + .../com/arialyy/aria/core/AriaConfig.java | 199 ++++++++ .../com/arialyy/aria/core}/FtpUrlEntity.java | 3 +- .../com/arialyy/aria/core}/ProtocolType.java | 2 +- .../arialyy/aria/core/TaskOptionParams.java | 74 +++ .../com/arialyy/aria/core}/TaskRecord.java | 2 +- .../com/arialyy/aria/core}/ThreadRecord.java | 2 +- .../arialyy/aria/core/common}/AbsEntity.java | 4 +- .../aria/core/common}/AbsGroupEntity.java | 2 +- .../aria/core/common}/AbsNormalEntity.java | 4 +- .../core/common/AbsRecordHandlerAdapter.java | 41 ++ .../aria/core/common/CompleteInfo.java | 6 +- .../aria/core/common/RecordHandler.java | 208 ++++++++ .../aria/core/common/RecordHelper.java | 138 ++++++ .../arialyy/aria/core/common/RequestEnum.java | 0 .../aria/core/common/SubThreadConfig.java | 7 +- .../arialyy/aria/core/config/AppConfig.java | 0 .../arialyy/aria/core/config/BaseConfig.java | 4 +- .../aria/core/config/BaseTaskConfig.java | 6 +- .../arialyy/aria/core/config/ConfigType.java | 0 .../aria/core/config/Configuration.java | 6 +- .../aria/core/config/DGroupConfig.java | 13 +- .../aria/core/config/DownloadConfig.java | 13 +- .../aria/core/config/TTaskConfigAdapeter.java | 0 .../aria/core/config/UploadConfig.java | 13 +- .../arialyy/aria/core/config/XMLReader.java | 0 .../core/download}/AbsGroupTaskWrapper.java | 5 +- .../aria/core/download/DGEntityWrapper.java | 2 + .../aria/core/download/DGTaskWrapper.java | 1 - .../aria/core/download/DTaskWrapper.java | 33 +- .../aria/core/download/DownloadEntity.java | 6 +- .../core/download/DownloadGroupEntity.java | 6 +- .../aria/core/download}/M3U8Entity.java | 8 +- .../aria/core/event/DGMaxNumEvent.java | 21 +- .../arialyy/aria/core/event/DMaxNumEvent.java | 28 ++ .../arialyy/aria/core/event/DSpeedEvent.java | 4 +- .../arialyy/aria/core/event/ErrorEvent.java | 0 .../com/arialyy/aria/core/event/Event.java | 0 .../aria/core/event/EventMethodInfo.java | 0 .../arialyy/aria/core/event/EventMsgUtil.java | 0 .../aria/core/event/PeerIndexEvent.java | 0 .../arialyy/aria/core/event/UMaxNumEvent.java | 28 ++ .../arialyy/aria/core/event/USpeedEvent.java | 25 + .../aria/core}/group/AbsGroupUtil.java | 98 ++-- .../aria/core/group/AbsSubDLoadUtil.java | 141 ++++++ .../aria/core}/group/ChildDLoadListener.java | 15 +- .../aria/core}/group/GroupRunState.java | 40 +- .../aria/core/group}/GroupSendParams.java | 5 +- .../arialyy/aria/core}/group/ISubQueue.java | 8 +- .../aria/core}/group/SimpleSchedulers.java | 20 +- .../aria/core}/group/SimpleSubQueue.java | 36 +- .../com/arialyy/aria/core/inf/EventFlag.java | 26 + .../com/arialyy/aria/core/inf/IEntity.java | 0 .../arialyy/aria/core/inf/IEventHandler.java | 25 + .../aria/core/inf/IOptionConstant.java | 52 ++ .../arialyy/aria/core/inf/IRecordHandler.java | 47 ++ .../aria/core/inf/IRecordHandlerAdapter.java | 55 +++ .../arialyy/aria/core/inf/ITaskOption.java | 2 +- .../arialyy/aria/core/inf}/IThreadState.java | 2 +- .../com/arialyy/aria/core/inf}/IUtil.java | 2 +- .../aria/core/inf}/OnFileInfoCallback.java | 5 +- .../com/arialyy/aria/core/inf}/Suggest.java | 2 +- .../aria/core/inf/TaskSchedulerType.java | 0 .../aria/core/listener}/BaseDListener.java | 23 +- .../aria/core/listener}/BaseListener.java | 12 +- .../aria/core/listener}/BaseUListener.java | 17 +- .../core/listener}/DownloadGroupListener.java | 19 +- .../aria/core/listener}/IDGroupListener.java | 6 +- .../aria/core/listener/IDLoadListener.java | 4 +- .../aria/core/listener}/IEventListener.java | 2 +- .../aria/core/listener}/ISchedulers.java | 4 +- .../aria/core/listener}/IUploadListener.java | 2 +- .../aria/core/listener}/UploadListener.java | 3 +- .../arialyy/aria/core/loader/AbsLoader.java | 72 +-- .../aria/core/loader/AbsNormalLoaderUtil.java | 152 ++++++ .../aria/core/loader/ILoaderAdapter.java | 48 ++ .../aria/core/loader/NormalLoader.java | 108 +++-- .../aria/core/loader}/ThreadStateManager.java | 11 +- .../aria/core/manager/ThreadTaskManager.java | 17 +- .../core/processor}/FtpInterceptHandler.java | 2 +- .../processor}/IBandWidthUrlConverter.java | 6 +- .../processor}/IFtpUploadInterceptor.java | 5 +- .../core/processor}/IHttpFileLenAdapter.java | 7 +- .../core/processor}/ILiveTsUrlConverter.java | 6 +- .../aria/core/processor}/ITsMergeHandler.java | 6 +- .../core/processor}/IVodTsUrlConverter.java | 5 +- .../arialyy/aria/core/task}/AbsGroupTask.java | 5 +- .../core/task/AbsNormalLoaderAdapter.java | 48 ++ .../com/arialyy/aria/core/task}/AbsTask.java | 27 +- .../aria/core/task/AbsThreadTaskAdapter.java | 133 +++++ .../aria/core/task}/DownloadGroupTask.java | 30 +- .../arialyy/aria/core/task}/DownloadTask.java | 54 +-- .../com/arialyy/aria/core/task}/ITask.java | 8 +- .../arialyy/aria/core/task/IThreadTask.java | 90 ++++ .../aria/core/task/IThreadTaskAdapter.java | 42 ++ .../aria/core/task/IThreadTaskObserver.java | 56 +++ .../arialyy/aria/core/task/ThreadTask.java | 238 +++++---- .../arialyy/aria/core/task}/UploadTask.java | 19 +- .../aria/core/upload/UTaskWrapper.java | 2 +- .../aria/core/upload/UploadEntity.java | 5 +- .../aria/core/wrapper}/AbsTaskWrapper.java | 79 ++- .../aria/core/wrapper}/ITaskWrapper.java | 25 +- .../aria/core/wrapper}/RecordWrapper.java | 4 +- .../aria/exception/AriaIOException.java | 0 .../arialyy/aria/exception/BaseException.java | 0 .../arialyy/aria/exception/FileException.java | 0 .../aria/exception/FileNotFoundException.java | 0 .../arialyy/aria/exception/M3U8Exception.java | 0 .../aria/exception/ParamException.java | 0 .../arialyy/aria/exception/TaskException.java | 0 .../com/arialyy/aria/orm/AbsDbWrapper.java | 0 .../com/arialyy/aria/orm/AbsDelegate.java | 0 .../com/arialyy/aria/orm/ActionPolicy.java | 0 .../java/com/arialyy/aria/orm/DBConfig.java | 10 +- .../com/arialyy/aria/orm/DatabaseContext.java | 0 .../java/com/arialyy/aria/orm/DbEntity.java | 0 .../com/arialyy/aria/orm/DelegateCommon.java | 3 +- .../com/arialyy/aria/orm/DelegateFind.java | 7 +- .../com/arialyy/aria/orm/DelegateManager.java | 0 .../com/arialyy/aria/orm/DelegateUpdate.java | 3 +- .../com/arialyy/aria/orm/DelegateWrapper.java | 0 .../java/com/arialyy/aria/orm/SqlHelper.java | 0 .../java/com/arialyy/aria/orm/SqlUtil.java | 0 .../arialyy/aria/orm/annotation/Default.java | 0 .../arialyy/aria/orm/annotation/Foreign.java | 0 .../arialyy/aria/orm/annotation/Ignore.java | 0 .../com/arialyy/aria/orm/annotation/Many.java | 0 .../arialyy/aria/orm/annotation/NoNull.java | 0 .../com/arialyy/aria/orm/annotation/One.java | 0 .../arialyy/aria/orm/annotation/Primary.java | 0 .../arialyy/aria/orm/annotation/Table.java | 0 .../arialyy/aria/orm/annotation/Unique.java | 0 .../arialyy/aria/orm/annotation/Wrapper.java | 0 .../main/java/com/arialyy/aria/util/ALog.java | 0 .../arialyy/aria/util/AriaCrashHandler.java | 0 .../arialyy/aria/util}/BandwidthLimiter.java | 2 +- .../aria/util/BufferedRandomAccessFile.java | 0 .../java/com/arialyy/aria/util/CheckUtil.java | 41 +- .../com/arialyy/aria/util/CommonUtil.java | 83 ++-- .../com/arialyy/aria/util/ComponentUtil.java | 243 ++++++++++ .../com/arialyy/aria/util/DbDataHelper.java | 4 +- .../java/com/arialyy/aria/util/ErrorHelp.java | 4 +- .../java/com/arialyy/aria/util/FileUtil.java | 6 +- .../java/com/arialyy/aria/util/NetUtils.java | 7 +- .../com/arialyy/aria/util/RecordUtil.java | 40 +- .../java/com/arialyy/aria/util/Regular.java | 0 .../com/arialyy/aria/util/SSLContextUtil.java | 8 +- .../com/arialyy/aria/util/WeakHandler.java | 0 .../src/main/res/values/strings.xml | 3 + app/build.gradle | 2 +- app/src/main/assets/help_code/FtpUpload.java | 2 +- .../assets/help_code/KotlinHttpDownload.kt | 24 +- .../com/arialyy/simple/DbTestActivity.java | 5 +- .../simple/core/download/DownloadDialog.java | 2 +- .../core/download/DownloadDialogFragment.java | 2 +- .../core/download/DownloadPopupWindow.java | 2 +- .../core/download/FtpDownloadActivity.java | 4 +- .../download/HighestPriorityActivity.java | 4 +- .../core/download/HttpDownloadModule.java | 2 +- .../core/download/KotlinDownloadActivity.kt | 41 +- .../core/download/SimpleNotification.java | 2 +- .../core/download/SingleTaskActivity.java | 6 +- .../download/fragment/DownloadFragment.java | 2 +- .../download/group/ChildHandleDialog.java | 2 +- .../download/group/DownloadGroupActivity.java | 4 +- .../group/FTPDirDownloadActivity.java | 2 +- .../download/m3u8/M3U8LiveDLoadActivity.java | 6 +- .../download/m3u8/M3U8VodDLoadActivity.java | 20 +- .../download/m3u8/VideoPlayerFragment.java | 2 +- .../core/download/mutil/DownloadAdapter.java | 4 +- .../download/mutil/MultiDownloadActivity.java | 8 +- .../download/mutil/MultiTaskActivity.java | 6 +- .../download/service/DownloadService.java | 2 +- .../simple/core/test/AnyRunActivity.java | 1 - .../simple/core/upload/FtpUploadActivity.java | 6 +- .../core/upload/HttpUploadActivity.java | 2 +- .../arialyy/simple/modlue/AnyRunnModule.java | 4 +- .../java/com/arialyy/simple/util/AppUtil.java | 2 +- build.gradle | 4 +- settings.gradle | 3 +- 337 files changed, 5153 insertions(+), 3217 deletions(-) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/command/CmdHelper.java rename Aria/src/main/java/com/arialyy/aria/core/common/{ftp => }/FTPSDelegate.java (74%) rename Aria/src/main/java/com/arialyy/aria/core/common/{ftp => }/FtpDelegate.java (80%) rename Aria/src/main/java/com/arialyy/aria/core/common/{http => }/HttpDelegate.java (58%) delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/common/RecordHandler.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/group/SubDLoadUtil.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodUtil.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java rename Aria/src/main/java/com/arialyy/aria/core/queue/{DownloadGroupTaskQueue.java => DGroupTaskQueue.java} (75%) rename Aria/src/main/java/com/arialyy/aria/core/queue/{DownloadTaskQueue.java => DTaskQueue.java} (85%) rename Aria/src/main/java/com/arialyy/aria/core/queue/{UploadTaskQueue.java => UTaskQueue.java} (74%) delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java delete mode 100644 AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java delete mode 100644 AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java delete mode 100644 AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java rename {AriaFtpComponent => FtpComponent}/.gitignore (100%) create mode 100644 FtpComponent/build.gradle rename {AriaFtpComponent => FtpComponent}/consumer-rules.pro (100%) rename {AriaFtpComponent => FtpComponent}/proguard-rules.pro (100%) rename {AriaFtpComponent => FtpComponent}/src/main/AndroidManifest.xml (100%) rename {Aria/src/main/java/com/arialyy/aria/core/common => FtpComponent/src/main/java/com/arialyy/aria}/ftp/AbsFtpInfoThread.java (90%) rename Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpThreadTask.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java (84%) rename {Aria/src/main/java/com/arialyy/aria/core/download/group => FtpComponent/src/main/java/com/arialyy/aria/ftp}/FtpDirInfoThread.java (79%) create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpTaskConfig.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpTaskOption.java (86%) rename {Aria/src/main/java/com/arialyy/aria/core/common => FtpComponent/src/main/java/com/arialyy/aria}/ftp/SSLSessionReuseFTPSClient.java (97%) rename Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDFileInfoThread.java (83%) create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java rename Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java (60%) rename Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java (62%) create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/download/SubDLoaderUtil.java rename {Aria/src/main/java/com/arialyy/aria/core/upload/uploader => FtpComponent/src/main/java/com/arialyy/aria/ftp/upload}/FtpFISAdapter.java (97%) rename Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java (86%) create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java => FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java (63%) rename {AriaFtpComponent => FtpComponent}/src/main/res/values/strings.xml (100%) create mode 100644 HttpComponent/.gitignore create mode 100644 HttpComponent/build.gradle create mode 100644 HttpComponent/consumer-rules.pro create mode 100644 HttpComponent/proguard-rules.pro create mode 100644 HttpComponent/src/main/AndroidManifest.xml create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/BaseHttpThreadTaskAdapter.java rename {Aria/src/main/java/com/arialyy/aria/core/download/downloader => HttpComponent/src/main/java/com/arialyy/aria/http}/ChunkedInputStream.java (98%) rename {Aria/src/main/java/com/arialyy/aria/core/download/downloader => HttpComponent/src/main/java/com/arialyy/aria/http}/ConnectionHelp.java (92%) rename {Aria/src/main/java/com/arialyy/aria/core/download/downloader => HttpComponent/src/main/java/com/arialyy/aria/http}/HttpFileInfoThread.java (90%) create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/common/http/HttpTaskConfig.java => HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/download/group/DGroupUtil.java => HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java (67%) create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java rename Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java => HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java (70%) create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/download/SubDLoaderUtil.java create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java rename Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java => HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java (75%) create mode 100644 HttpComponent/src/main/res/values/strings.xml create mode 100644 M3U8Component/.gitignore create mode 100644 M3U8Component/build.gradle create mode 100644 M3U8Component/consumer-rules.pro create mode 100644 M3U8Component/proguard-rules.pro create mode 100644 M3U8Component/src/main/AndroidManifest.xml rename {Aria/src/main/java/com/arialyy/aria/core/download => M3U8Component/src/main/java/com/arialyy/aria}/m3u8/BaseM3U8Loader.java (77%) rename {Aria/src/main/java/com/arialyy/aria/util => M3U8Component/src/main/java/com/arialyy/aria/m3u8}/IdGenerator.java (98%) rename {Aria/src/main/java/com/arialyy/aria/core/download => M3U8Component/src/main/java/com/arialyy/aria}/m3u8/M3U8InfoThread.java (84%) rename {Aria/src/main/java/com/arialyy/aria/core/download => M3U8Component/src/main/java/com/arialyy/aria/m3u8}/M3U8Listener.java (82%) create mode 100644 M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java => M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java (86%) rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8ThreadTask.java => M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java (70%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => M3U8Component/src/main/java/com/arialyy/aria/m3u8/live}/M3U8LiveLoader.java (83%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => M3U8Component/src/main/java/com/arialyy/aria/m3u8/live}/M3U8LiveUtil.java (53%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod}/M3U8VodLoader.java (88%) create mode 100644 M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java create mode 100644 M3U8Component/src/main/res/values/strings.xml create mode 100644 PublicComponent/.gitignore rename {AriaFtpComponent => PublicComponent}/build.gradle (89%) create mode 100644 PublicComponent/consumer-rules.pro create mode 100644 PublicComponent/proguard-rules.pro create mode 100644 PublicComponent/src/main/AndroidManifest.xml create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java rename {Aria/src/main/java/com/arialyy/aria/core/common/ftp => PublicComponent/src/main/java/com/arialyy/aria/core}/FtpUrlEntity.java (96%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core}/ProtocolType.java (96%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core}/TaskRecord.java (98%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core}/ThreadRecord.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/common}/AbsEntity.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/common}/AbsGroupEntity.java (98%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/common}/AbsNormalEntity.java (97%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java (87%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/common/RequestEnum.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java (86%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/AppConfig.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/BaseConfig.java (91%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java (97%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/ConfigType.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/Configuration.java (95%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java (88%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java (86%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/TTaskConfigAdapeter.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/UploadConfig.java (75%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/config/XMLReader.java (100%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/download}/AbsGroupTaskWrapper.java (90%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java (92%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java (97%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java (80%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java (97%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java (91%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => PublicComponent/src/main/java/com/arialyy/aria/core/download}/M3U8Entity.java (95%) rename Aria/src/main/java/com/arialyy/aria/core/common/AEvent.java => PublicComponent/src/main/java/com/arialyy/aria/core/event/DGMaxNumEvent.java (63%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/event/DMaxNumEvent.java rename Aria/src/main/java/com/arialyy/aria/core/event/SpeedEvent.java => PublicComponent/src/main/java/com/arialyy/aria/core/event/DSpeedEvent.java (92%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/event/ErrorEvent.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/event/Event.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/event/EventMethodInfo.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/event/EventMsgUtil.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/event/PeerIndexEvent.java (100%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/event/UMaxNumEvent.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/event/USpeedEvent.java rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/AbsGroupUtil.java (80%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/ChildDLoadListener.java (91%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/GroupRunState.java (79%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/group}/GroupSendParams.java (87%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/ISubQueue.java (91%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/SimpleSchedulers.java (92%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core}/group/SimpleSubQueue.java (81%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/inf/EventFlag.java rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/inf/IEntity.java (100%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEventHandler.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/inf/IOptionConstant.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/inf/ITaskConfig.java => PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java (95%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/inf}/IThreadState.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/inf}/IUtil.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/inf}/OnFileInfoCallback.java (87%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/inf}/Suggest.java (95%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java (100%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/BaseDListener.java (73%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/BaseListener.java (94%) rename {Aria/src/main/java/com/arialyy/aria/core/upload => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/BaseUListener.java (72%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/DownloadGroupListener.java (90%) rename {Aria/src/main/java/com/arialyy/aria/core/download/group => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/IDGroupListener.java (90%) rename Aria/src/main/java/com/arialyy/aria/core/inf/IDownloadListener.java => PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDLoadListener.java (90%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/IEventListener.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/scheduler => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/ISchedulers.java (97%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/IUploadListener.java (94%) rename {Aria/src/main/java/com/arialyy/aria/core/upload => PublicComponent/src/main/java/com/arialyy/aria/core/listener}/UploadListener.java (93%) rename Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java => PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java (82%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java rename Aria/src/main/java/com/arialyy/aria/core/common/NormalFileer.java => PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java (60%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/loader}/ThreadStateManager.java (93%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java (92%) rename {Aria/src/main/java/com/arialyy/aria/core/common/ftp => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/FtpInterceptHandler.java (98%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/IBandWidthUrlConverter.java (89%) rename {Aria/src/main/java/com/arialyy/aria/core/common/ftp => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/IFtpUploadInterceptor.java (91%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/IHttpFileLenAdapter.java (86%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/ILiveTsUrlConverter.java (88%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/ITsMergeHandler.java (85%) rename {Aria/src/main/java/com/arialyy/aria/core/download/m3u8 => PublicComponent/src/main/java/com/arialyy/aria/core/processor}/IVodTsUrlConverter.java (90%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/task}/AbsGroupTask.java (90%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/task}/AbsTask.java (86%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core/task}/DownloadGroupTask.java (67%) rename {Aria/src/main/java/com/arialyy/aria/core/download => PublicComponent/src/main/java/com/arialyy/aria/core/task}/DownloadTask.java (55%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/task}/ITask.java (90%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java rename Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java => PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java (78%) rename {aria/src/main/java/com/arialyy/aria/core/upload => PublicComponent/src/main/java/com/arialyy/aria/core/task}/UploadTask.java (77%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java (96%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java (94%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/wrapper}/AbsTaskWrapper.java (77%) rename {Aria/src/main/java/com/arialyy/aria/core/inf => PublicComponent/src/main/java/com/arialyy/aria/core/wrapper}/ITaskWrapper.java (80%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/core/wrapper}/RecordWrapper.java (91%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/AriaIOException.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/BaseException.java (100%) rename {aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/FileException.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/FileNotFoundException.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/M3U8Exception.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/ParamException.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/exception/TaskException.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/AbsDbWrapper.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/AbsDelegate.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/ActionPolicy.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DBConfig.java (84%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DatabaseContext.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DbEntity.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DelegateCommon.java (98%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DelegateFind.java (99%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DelegateManager.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java (98%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/SqlHelper.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/SqlUtil.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Default.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Many.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/One.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Primary.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Table.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Unique.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/ALog.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java (100%) rename {Aria/src/main/java/com/arialyy/aria/core/common => PublicComponent/src/main/java/com/arialyy/aria/util}/BandwidthLimiter.java (98%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/CheckUtil.java (85%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/CommonUtil.java (91%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/DbDataHelper.java (97%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/ErrorHelp.java (96%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/FileUtil.java (99%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/NetUtils.java (96%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/RecordUtil.java (89%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/Regular.java (100%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/SSLContextUtil.java (97%) rename {Aria => PublicComponent}/src/main/java/com/arialyy/aria/util/WeakHandler.java (100%) create mode 100644 PublicComponent/src/main/res/values/strings.xml diff --git a/AppFrame/src/main/java/com/arialyy/frame/cache/DiskLruCache.java b/AppFrame/src/main/java/com/arialyy/frame/cache/DiskLruCache.java index 48c60fb0..1b1f25a5 100644 --- a/AppFrame/src/main/java/com/arialyy/frame/cache/DiskLruCache.java +++ b/AppFrame/src/main/java/com/arialyy/frame/cache/DiskLruCache.java @@ -245,7 +245,6 @@ public final class DiskLruCache implements Closeable { /** * Recursively delete everything in {@code dir}. */ - // TODO: this should specify paths as Strings rather than as Files public static void deleteContents(File dir) throws IOException { File[] files = dir.listFiles(); if (files == null) { diff --git a/Aria/build.gradle b/Aria/build.gradle index 39b76600..08cb9b35 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 329 - versionName "3.2.9" + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName } buildTypes { release { @@ -26,9 +26,13 @@ dependencies { testImplementation 'junit:junit:4.12' implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" api project(':AriaAnnotations') - api 'com.arialyy.aria:aria-ftp-plug:1.0.4' // 打包时用这个 + api project(path: ':PublicComponent') + + api 'com.arialyy.aria:aria-ftp-plug:1.0.4' + implementation project(path: ':HttpComponent') // 打包时用这个 // compile project(':AriaFtpPlug') +// implementation project(path: ':AriaFtpComponent') } apply from: 'bintray-release.gradle' diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index 6ace735b..3461574a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -34,18 +34,16 @@ import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import com.arialyy.aria.core.command.CommandManager; import com.arialyy.aria.core.common.QueueMod; -import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.config.AppConfig; -import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.DGroupConfig; import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.config.UploadConfig; -import com.arialyy.aria.core.config.XMLReader; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.IReceiver; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadReceiver; @@ -53,19 +51,13 @@ import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.DelegateWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.AriaCrashHandler; -import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import org.xml.sax.SAXException; /** * Created by lyy on 2016/12/1. https://github.com/AriaLyy/Aria Aria管理器,任务操作在这里执行 @@ -73,12 +65,8 @@ import org.xml.sax.SAXException; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class AriaManager { private static final String TAG = "AriaManager"; private static final Object LOCK = new Object(); - public static final String DOWNLOAD_TEMP_DIR = "/Aria/temp/download/"; - public static final String UPLOAD_TEMP_DIR = "/Aria/temp/upload/"; - /** - * 是否已经联网,true 已经联网 - */ - private static boolean isConnectedNet = true; + + @SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null; private Map mReceivers = new ConcurrentHashMap<>(); @@ -87,12 +75,9 @@ import org.xml.sax.SAXException; */ private Map> mSubClass = new ConcurrentHashMap<>(); private static Context APP; - private DownloadConfig mDConfig; - private UploadConfig mUConfig; - private AppConfig mAConfig; - private DGroupConfig mDGConfig; private Handler mAriaHandler; private DelegateWrapper mDbWrapper; + private AriaConfig mConfig; private AriaManager(Context context) { APP = context.getApplicationContext(); @@ -118,55 +103,18 @@ import org.xml.sax.SAXException; } private void initData() { + mConfig = AriaConfig.init(APP); initDb(APP); regAppLifeCallback(APP); - initConfig(); initAria(); amendTaskState(); - regNetCallBack(APP); } public Context getAPP() { return APP; } - /** - * 注册网络监听,只有配置了检查网络{@link AppConfig#isNetCheck()}才会注册事件 - */ - private void regNetCallBack(Context context) { - if (!getAppConfig().isNetCheck()) { - return; - } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - return; - } - ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm == null) { - return; - } - - NetworkRequest.Builder builder = new NetworkRequest.Builder(); - NetworkRequest request = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .build(); - cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { - - @Override public void onLost(Network network) { - super.onLost(network); - isConnectedNet = false; - ALog.d(TAG, "onLost"); - } - - @Override public void onAvailable(Network network) { - super.onAvailable(network); - ALog.d(TAG, "onAvailable"); - isConnectedNet = true; - } - }); - } /** * 初始化数据库 @@ -186,10 +134,11 @@ import org.xml.sax.SAXException; } private void initAria() { - if (mAConfig.getUseAriaCrashHandler()) { + AppConfig appConfig = mConfig.getAConfig(); + if (appConfig.getUseAriaCrashHandler()) { Thread.setDefaultUncaughtExceptionHandler(new AriaCrashHandler()); } - mAConfig.setLogLevel(mAConfig.getLogLevel()); + appConfig.setLogLevel(appConfig.getLogLevel()); CommandManager.init(); } @@ -217,9 +166,7 @@ import org.xml.sax.SAXException; return mAriaHandler; } - public boolean isConnectedNet() { - return isConnectedNet; - } + public Map getReceiver() { return mReceivers; @@ -237,7 +184,7 @@ import org.xml.sax.SAXException; * @deprecated 后续版本会删除该api */ @Deprecated public AriaManager setUploadQueueMod(QueueMod mod) { - mUConfig.setQueueMod(mod.tag); + mConfig.getUConfig().setQueueMod(mod.tag); return this; } @@ -253,7 +200,7 @@ import org.xml.sax.SAXException; * @deprecated 后续版本会删除该api */ @Deprecated public AriaManager setDownloadQueueMod(QueueMod mod) { - mDConfig.setQueueMod(mod.tag); + mConfig.getDConfig().setQueueMod(mod.tag); return this; } @@ -267,7 +214,7 @@ import org.xml.sax.SAXException; * */ public DownloadConfig getDownloadConfig() { - return mDConfig; + return mConfig.getDConfig(); } /** @@ -280,14 +227,14 @@ import org.xml.sax.SAXException; * */ public UploadConfig getUploadConfig() { - return mUConfig; + return mConfig.getUConfig(); } /** * 获取APP配置 */ public AppConfig getAppConfig() { - return mAConfig; + return mConfig.getAConfig(); } /** @@ -300,7 +247,7 @@ import org.xml.sax.SAXException; * */ public DGroupConfig getDGroupConfig() { - return mDGConfig; + return mConfig.getDGConfig(); } /** @@ -335,13 +282,13 @@ import org.xml.sax.SAXException; public void delRecord(int type, String key, boolean removeFile) { switch (type) { case 1: // 删除普通任务记录 - RecordUtil.delTaskRecord(key, RecordHandler.TYPE_DOWNLOAD, removeFile, true); + RecordUtil.delTaskRecord(key, IRecordHandler.TYPE_DOWNLOAD, removeFile, true); break; case 2: RecordUtil.delGroupTaskRecord(key, removeFile); break; case 3: - RecordUtil.delTaskRecord(key, RecordHandler.TYPE_UPLOAD); + RecordUtil.delTaskRecord(key, IRecordHandler.TYPE_UPLOAD); break; } } @@ -442,58 +389,6 @@ import org.xml.sax.SAXException; return String.format("%s_%s_%s", obj.getClass().getName(), type, obj.hashCode()); } - /** - * 初始化配置文件 - */ - private void initConfig() { - mDConfig = Configuration.getInstance().downloadCfg; - mUConfig = Configuration.getInstance().uploadCfg; - mAConfig = Configuration.getInstance().appCfg; - mDGConfig = Configuration.getInstance().dGroupCfg; - - File xmlFile = new File(APP.getFilesDir().getPath() + Configuration.XML_FILE); - File tempDir = new File(APP.getFilesDir().getPath() + "/temp"); - if (!xmlFile.exists()) { - loadConfig(); - } else { - try { - String md5Code = CommonUtil.getFileMD5(xmlFile); - File file = new File(APP.getFilesDir().getPath() + "/temp.xml"); - if (file.exists()) { - file.delete(); - } - CommonUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"), - file.getPath()); - if (!CommonUtil.checkMD5(md5Code, file) || !Configuration.getInstance().configExists()) { - loadConfig(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - if (tempDir.exists()) { - File newDir = new File(APP.getFilesDir().getPath() + DOWNLOAD_TEMP_DIR); - newDir.mkdirs(); - tempDir.renameTo(newDir); - } - } - - /** - * 加载配置文件 - */ - private void loadConfig() { - try { - XMLReader helper = new XMLReader(); - SAXParserFactory factory = SAXParserFactory.newInstance(); - SAXParser parser = factory.newSAXParser(); - parser.parse(APP.getAssets().open("aria_config.xml"), helper); - CommonUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"), - APP.getFilesDir().getPath() + Configuration.XML_FILE); - } catch (ParserConfigurationException | IOException | SAXException e) { - ALog.e(TAG, e.toString()); - } - } - /** * 注册APP生命周期回调 */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java index 4867c87b..85ee7650 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmd.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.queue.AbsTaskQueue; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java index 14caef9a..2af0e2ff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by AriaL on 2017/6/29. diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsGroupCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsGroupCmd.java index be976d12..dc97c141 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsGroupCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsGroupCmd.java @@ -15,11 +15,11 @@ */ package com.arialyy.aria.core.command; +import com.arialyy.aria.core.download.AbsGroupTaskWrapper; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.inf.AbsGroupTask; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.task.AbsGroupTask; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -39,7 +39,7 @@ public abstract class AbsGroupCmd extends AbsCmd< mTaskWrapper = entity; TAG = CommonUtil.getClassName(this); if (entity instanceof DGTaskWrapper) { - mQueue = DownloadGroupTaskQueue.getInstance(); + mQueue = DGroupTaskQueue.getInstance(); isDownloadCmd = true; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsNormalCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsNormalCmd.java index 10494a6f..10a11a96 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsNormalCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsNormalCmd.java @@ -18,14 +18,14 @@ package com.arialyy.aria.core.command; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.ITask; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -54,19 +54,19 @@ public abstract class AbsNormalCmd extends AbsCmd { ALog.e(TAG, "任务类型错误,任务类型应该为ICM.TASK_TYPE_DOWNLOAD"); return; } - mQueue = DownloadTaskQueue.getInstance(); + mQueue = DTaskQueue.getInstance(); } else if (taskType == ITask.DOWNLOAD_GROUP) { if (!(entity instanceof DGTaskWrapper)) { ALog.e(TAG, "任务类型错误,任务类型应该为ICM.TASK_TYPE_DOWNLOAD_GROUP"); return; } - mQueue = DownloadGroupTaskQueue.getInstance(); + mQueue = DGroupTaskQueue.getInstance(); } else if (taskType == ITask.UPLOAD) { if (!(entity instanceof UTaskWrapper)) { ALog.e(TAG, "任务类型错误,任务类型应该为ICM.TASK_TYPE_UPLOAD"); return; } - mQueue = UploadTaskQueue.getInstance(); + mQueue = UTaskQueue.getInstance(); } else { ALog.e(TAG, "任务类型错误,任务类型应该为ICM.TASK_TYPE_DOWNLOAD、TASK_TYPE_DOWNLOAD_GROUP、TASK_TYPE_UPLOAD"); return; diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java index 299ba3f8..03046ff8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AddCmd.java @@ -16,8 +16,8 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/CancelAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/CancelAllCmd.java index 52cb7de5..f589d984 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/CancelAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/CancelAllCmd.java @@ -20,11 +20,11 @@ import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; @@ -104,11 +104,11 @@ final public class CancelAllCmd extends AbsNormalCmd AbsNormalCmd createNormalCmd(T entity, int cmd, + int taskType) { + return NormalCmdFactory.getInstance().createCmd(entity, cmd, taskType); + } + + /** + * 创建任务组命令 + * + * @param childUrl 子任务url + */ + public static AbsGroupCmd createGroupCmd(T entity, int cmd, + String childUrl) { + return GroupCmdFactory.getInstance().createCmd(entity, cmd, childUrl); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStartCmd.java index 4cd0e163..bb16de55 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStartCmd.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; +import com.arialyy.aria.core.download.AbsGroupTaskWrapper; /** * Created by AriaL on 2017/6/29. diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStopCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStopCmd.java index 78b8b63f..7cdcd226 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStopCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/DGSubStopCmd.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; +import com.arialyy.aria.core.download.AbsGroupTaskWrapper; /** * Created by AriaL on 2017/6/29. diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java index 0edbfec9..e1241d65 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/GroupCmdFactory.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; +import com.arialyy.aria.core.download.AbsGroupTaskWrapper; /** * Created by AriaL on 2017/6/29. 任务组子任务控制命令 diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java index 42fe3c20..692d083c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java @@ -16,9 +16,9 @@ package com.arialyy.aria.core.command; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.NetUtils; @@ -50,7 +50,7 @@ final class HighestPriorityCmd extends AbsNormalCmd task = (DownloadTask) createTask(); } if (task != null) { - ((DownloadTaskQueue) mQueue).setTaskHighestPriority(task); + ((DTaskQueue) mQueue).setTaskHighestPriority(task); } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java index 767a7fdd..8ba64474 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by Lyy on 2016/9/23. diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java index bc3d8629..10598993 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java @@ -15,8 +15,8 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.TaskSchedulerType; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java index f29e79f6..5a486e5f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java @@ -5,14 +5,14 @@ import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; @@ -112,11 +112,11 @@ final class ResumeAllCmd extends AbsNormalCmd { for (AbsTaskWrapper te : mWaitList) { if (te instanceof DTaskWrapper) { - mQueue = DownloadTaskQueue.getInstance(); + mQueue = DTaskQueue.getInstance(); } else if (te instanceof UTaskWrapper) { - mQueue = UploadTaskQueue.getInstance(); + mQueue = UTaskQueue.getInstance(); } else if (te instanceof DGTaskWrapper) { - mQueue = DownloadGroupTaskQueue.getInstance(); + mQueue = DGroupTaskQueue.getInstance(); } if (mQueue.getCurrentExePoolNum() < maxTaskNum) { startTask(createTask(te)); diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java index 5aec44d8..8c520bf9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java @@ -22,16 +22,17 @@ import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -156,22 +157,24 @@ final class StartCmd extends AbsNormalCmd { } private void handleTask(List waitList) { - for (AbsTaskWrapper te : waitList) { - if (te.getEntity() == null) continue; - AbsTask task = getTask(te.getKey()); + for (AbsTaskWrapper wrapper : waitList) { + if (wrapper.getEntity() == null) continue; + AbsTask task = getTask(wrapper.getKey()); if (task != null) continue; - if (te instanceof DTaskWrapper) { - if (te.getRequestType() == ITaskWrapper.D_FTP - || te.getRequestType() == ITaskWrapper.U_FTP) { - te.asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(te.getEntity().getKey())); + if (wrapper instanceof DTaskWrapper) { + if (wrapper.getRequestType() == ITaskWrapper.D_FTP + || wrapper.getRequestType() == ITaskWrapper.U_FTP) { + wrapper.getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, + CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey())); } - mQueue = DownloadTaskQueue.getInstance(); - } else if (te instanceof UTaskWrapper) { - mQueue = UploadTaskQueue.getInstance(); - } else if (te instanceof DGTaskWrapper) { - mQueue = DownloadGroupTaskQueue.getInstance(); + mQueue = DTaskQueue.getInstance(); + } else if (wrapper instanceof UTaskWrapper) { + mQueue = UTaskQueue.getInstance(); + } else if (wrapper instanceof DGTaskWrapper) { + mQueue = DGroupTaskQueue.getInstance(); } - createTask(te); + createTask(wrapper); } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java index 81966da4..bd95cbc8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StopAllCmd.java @@ -1,6 +1,6 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by AriaL on 2017/6/13. diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java index 57c04eb4..5032fc83 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StopCmd.java @@ -16,8 +16,8 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index bcaf9b10..0a4e505b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.common; import com.arialyy.aria.core.common.controller.INormalFeature; import com.arialyy.aria.core.common.controller.NormalController; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsNormalEntity; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.manager.TaskWrapperManager; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java index f1496339..439f0a40 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java @@ -19,7 +19,8 @@ import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.common.controller.FeatureController; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.CommonUtil; public abstract class BaseDelegate { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FTPSDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java similarity index 74% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/FTPSDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java index 3a610e18..1197c370 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FTPSDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.ProtocolType; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.ProtocolType; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.util.ALog; /** * D_FTP SSL/TSL 参数委托 @@ -32,8 +34,11 @@ public class FTPSDelegate extends BaseDelegate public FTPSDelegate(TARGET target, AbsTaskWrapper wrapper) { super(target, wrapper); - mUrlEntity = getTaskWrapper().asFtp().getUrlEntity(); - mUrlEntity.isFtps = true; + mUrlEntity = (FtpUrlEntity) getTaskWrapper().getOptionParams() + .getParam(IOptionConstant.ftpUrlEntity); + if (mUrlEntity != null) { + mUrlEntity.isFtps = true; + } } /** @@ -44,7 +49,8 @@ public class FTPSDelegate extends BaseDelegate @CheckResult(suggest = Suggest.TO_CONTROLLER) public FTPSDelegate setProtocol(@ProtocolType String protocol) { if (TextUtils.isEmpty(protocol)) { - throw new NullPointerException("协议为空"); + ALog.e(TAG, "设置协议失败,协议信息为空"); + return this; } mUrlEntity.protocol = protocol; return this; @@ -58,7 +64,8 @@ public class FTPSDelegate extends BaseDelegate @CheckResult(suggest = Suggest.TO_CONTROLLER) public FTPSDelegate setAlias(String keyAlias) { if (TextUtils.isEmpty(keyAlias)) { - throw new NullPointerException("别名为空"); + ALog.e(TAG, "设置证书别名失败,证书别名为空"); + return this; } mUrlEntity.keyAlias = keyAlias; return this; @@ -72,7 +79,8 @@ public class FTPSDelegate extends BaseDelegate @CheckResult(suggest = Suggest.TO_CONTROLLER) public FTPSDelegate setStorePass(String storePass) { if (TextUtils.isEmpty(storePass)) { - throw new NullPointerException("证书密码为空"); + ALog.e(TAG, "设置证书密码失败,证书密码为空"); + return this; } mUrlEntity.storePass = storePass; return this; @@ -86,7 +94,8 @@ public class FTPSDelegate extends BaseDelegate @CheckResult(suggest = Suggest.TO_CONTROLLER) public FTPSDelegate setStorePath(String storePath) { if (TextUtils.isEmpty(storePath)) { - throw new NullPointerException("证书路径为空"); + ALog.e(TAG, "设置证书路径失败,证书路径为空"); + return this; } mUrlEntity.storePath = storePath; return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java similarity index 80% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java index 2c019156..be681bd2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; import aria.apache.commons.net.ftp.FTPClientConfig; -import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.util.ALog; /** @@ -39,7 +40,7 @@ public class FtpDelegate extends BaseDelegate if (TextUtils.isEmpty(charSet)) { throw new NullPointerException("字符编码为空"); } - getTaskWrapper().asFtp().setCharSet(charSet); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.charSet, charSet); return this; } @@ -58,7 +59,13 @@ public class FtpDelegate extends BaseDelegate return this; } // urlEntity 不能在构造函数中获取,因为ftp上传时url是后于构造函数的 - FtpUrlEntity urlEntity = getTaskWrapper().asFtp().getUrlEntity(); + FtpUrlEntity urlEntity = + (FtpUrlEntity) getTaskWrapper().getOptionParams() + .getParam(IOptionConstant.ftpUrlEntity); + if (urlEntity == null) { + ALog.e(TAG, "设置登陆信息失败,FtpUrlEntity为空"); + return this; + } urlEntity.needLogin = true; urlEntity.user = userName; urlEntity.password = password; @@ -81,7 +88,7 @@ public class FtpDelegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public FtpDelegate setFtpClentConfig(FTPClientConfig config) { - getTaskWrapper().asFtp().setClientConfig(config); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.clientConfig, config); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/http/HttpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java similarity index 58% rename from Aria/src/main/java/com/arialyy/aria/core/common/http/HttpDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java index 10ebeea7..8e0d2505 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/http/HttpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java @@ -13,28 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.http; +package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; -import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.util.ALog; import java.net.Proxy; -import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; /** * HTTP协议处理 */ public class HttpDelegate extends BaseDelegate { + private Map params; + private Map headers; + public HttpDelegate(TARGET target, AbsTaskWrapper wrapper) { super(target, wrapper); } @@ -46,7 +46,7 @@ public class HttpDelegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public HttpDelegate setRequestType(RequestEnum requestEnum) { - getTaskWrapper().asHttp().setRequestEnum(requestEnum); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.requestEnum, requestEnum); return this; } @@ -55,7 +55,11 @@ public class HttpDelegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public HttpDelegate setParams(Map params) { - getTaskWrapper().asHttp().setParams(params); + if (this.params == null) { + this.params = new HashMap<>(); + } + this.params.putAll(params); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.params, this.params); return this; } @@ -68,12 +72,11 @@ public class HttpDelegate extends BaseDelegate ALog.d(TAG, "key 或value 为空"); return this; } - Map params = getTaskWrapper().asHttp().getParams(); if (params == null) { params = new HashMap<>(); - getTaskWrapper().asHttp().setParams(params); } params.put(key, value); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.params, params); return this; } @@ -82,7 +85,7 @@ public class HttpDelegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public HttpDelegate setFormFields(Map params) { - getTaskWrapper().asHttp().setFormFields(params); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.formFields, params); return this; } @@ -102,7 +105,11 @@ public class HttpDelegate extends BaseDelegate ALog.w(TAG, "设置header失败,header对应的value不能为null"); return this; } - addHeader(getTaskWrapper(), key, value); + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.put(key, value); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.headers, this.headers); return this; } @@ -118,7 +125,11 @@ public class HttpDelegate extends BaseDelegate ALog.w(TAG, "设置header失败,map没有header数据"); return this; } - addHeaders(getTaskWrapper(), headers); + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.putAll(headers); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.headers, this.headers); return this; } @@ -127,60 +138,7 @@ public class HttpDelegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public HttpDelegate setUrlProxy(Proxy proxy) { - getTaskWrapper().asHttp().setProxy(proxy); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.proxy, proxy); return this; } - - private void addHeader(AbsTaskWrapper taskWrapper, String key, String value) { - HttpTaskConfig taskDelegate = taskWrapper.asHttp(); - if (taskDelegate.getHeaders().get(key) == null) { - taskDelegate.getHeaders().put(key, value); - } else if (!taskDelegate.getHeaders().get(key).equals(value)) { - taskDelegate.getHeaders().put(key, value); - } - } - - private void addHeaders(AbsTaskWrapper taskWrapper, Map headers) { - HttpTaskConfig taskDelegate = taskWrapper.asHttp(); - /* - 两个map比较逻辑 - 1、比对key是否相同 - 2、如果key相同,比对value是否相同 - 3、只有当上面两个步骤中key 和 value都相同时才能任务两个map数据一致 - */ - boolean mapEquals = false; - if (taskDelegate.getHeaders().size() == headers.size()) { - int i = 0; - Set keys = taskDelegate.getHeaders().keySet(); - for (String key : keys) { - if (headers.containsKey(key)) { - i++; - } else { - break; - } - } - if (i == taskDelegate.getHeaders().size()) { - int j = 0; - Collection values = taskDelegate.getHeaders().values(); - for (String value : values) { - if (headers.containsValue(value)) { - j++; - } else { - break; - } - } - if (j == taskDelegate.getHeaders().size()) { - mapEquals = true; - } - } - } - - if (!mapEquals) { - taskDelegate.getHeaders().clear(); - Set keys = headers.keySet(); - for (String key : keys) { - taskDelegate.getHeaders().put(key, headers.get(key)); - } - } - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/Aria/src/main/java/com/arialyy/aria/core/common/RecordHandler.java deleted file mode 100644 index f39f4c59..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.common; - -import com.arialyy.aria.core.config.Configuration; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.m3u8.BaseM3U8Loader; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import com.arialyy.aria.util.DbDataHelper; -import com.arialyy.aria.util.FileUtil; -import com.arialyy.aria.util.RecordUtil; -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; - -/** - * 处理任务记录,分配线程区间 - */ -public class RecordHandler { - private final String TAG = "RecordHandler"; - - public static final int TYPE_DOWNLOAD = 1; - public static final int TYPE_UPLOAD = 2; - public static final int TYPE_M3U8_VOD = 3; - public static final int TYPE_M3U8_LIVE = 4; - - private static final String STATE = "_state_"; - private static final String RECORD = "_record_"; - /** - * 小于1m的文件不启用多线程 - */ - private static final long SUB_LEN = 1024 * 1024; - - /** - * 分块文件路径 - */ - public static final String SUB_PATH = "%s.%s.part"; - - @Deprecated private File mConfigFile; - private TaskRecord mTaskRecord; - private AbsTaskWrapper mTaskWrapper; - private AbsNormalEntity mEntity; - - RecordHandler(AbsTaskWrapper wrapper) { - mTaskWrapper = wrapper; - mEntity = (AbsNormalEntity) mTaskWrapper.getEntity(); - } - - /** - * 获取任务记录,如果任务记录存在,检查任务记录 - * 检查记录 对于分块任务: 子分块不存在或被删除,子线程将重新下载 - * 对于普通任务: 预下载文件不存在,则任务任务呗删除 - * 如果任务记录不存在或线程记录不存在,初始化记录 - * - * @return 任务记录 - */ - TaskRecord getRecord() { - mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); - if (mConfigFile.exists()) { - convertDb(); - } else { - mTaskRecord = DbDataHelper.getTaskRecord(getFilePath()); - if (mTaskRecord == null) { - initRecord(true); - } else { - if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { - initRecord(false); - } - - if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { - handleM3U8Record(); - } else if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { - ALog.i(TAG, "直播下载不处理历史记录"); - } else { - if (mTaskRecord.isBlock) { - handleBlockRecord(); - } else if (!mTaskWrapper.isSupportBP()) { - handleNoSupportBPRecord(); - } else if (!mTaskRecord.isBlock && mTaskRecord.threadNum > 1) { - handleNoBlockMultiThreadRecord(); - } else { - handleSingleThreadRecord(); - } - } - } - } - saveRecord(); - return mTaskRecord; - } - - /** - * 处理m3u8记录, - * 1、如果分片文件存在,并且分片文件的记录没有完成,则需要删除该分片文件 - * 2、如果记录显示已完成,但是分片文件不存在,则重新开始该分片 - * 3、如果记录显示已完成,并且文件存在,记录当前任务进度 - */ - private void handleM3U8Record() { - DTaskWrapper wrapper = (DTaskWrapper) mTaskWrapper; - String cacheDir = wrapper.asM3U8().getCacheDir(); - long currentProgress = 0; - int completeNum = 0; - for (ThreadRecord record : mTaskRecord.threadRecords) { - File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); - if (!record.isComplete) { - if (temp.exists()) { - temp.delete(); - } - record.startLocation = 0; - //ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId)); - } else { - if (!temp.exists()) { - record.startLocation = 0; - record.isComplete = false; - ALog.w(TAG, String.format("分片【%s】不存在,将重新下载该分片", record.threadId)); - } else { - completeNum++; - currentProgress += temp.length(); - } - } - } - wrapper.asM3U8().setCompleteNum(completeNum); - wrapper.getEntity().setCurrentProgress(currentProgress); - mTaskRecord.bandWidth = wrapper.asM3U8().getBandWidth(); - } - - /** - * 处理不支持断点的记录 - */ - private void handleNoSupportBPRecord() { - ThreadRecord tr = mTaskRecord.threadRecords.get(0); - tr.startLocation = 0; - tr.endLocation = mEntity.getFileSize(); - tr.taskKey = mTaskRecord.filePath; - tr.blockLen = tr.endLocation; - tr.isComplete = false; - } - - /** - * 处理为不分块的多线程任务 - */ - private void handleNoBlockMultiThreadRecord() { - File file = new File(mTaskRecord.filePath); - if (!file.exists()) { - ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); - DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); - initRecord(false); - } - } - - /** - * 处理单线程的任务的记录 - */ - private void handleSingleThreadRecord() { - File file = new File(mTaskRecord.filePath); - ThreadRecord tr = mTaskRecord.threadRecords.get(0); - if (!file.exists()) { - ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); - tr.startLocation = 0; - tr.isComplete = false; - tr.endLocation = mEntity.getFileSize(); - } else if (mTaskRecord.isOpenDynamicFile) { - if (file.length() > mEntity.getFileSize()) { - ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); - file.delete(); - tr.startLocation = 0; - tr.isComplete = false; - tr.endLocation = mEntity.getFileSize(); - } else if (file.length() == mEntity.getFileSize()) { - tr.isComplete = true; - } else { - if (file.length() != tr.startLocation) { - ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length())); - tr.startLocation = file.length(); - tr.isComplete = false; - } - } - } - mTaskWrapper.setNewTask(false); - } - - /** - * 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度 - */ - private void handleBlockRecord() { - // 默认线程分块长度 - long normalRectLen = mEntity.getFileSize() / mTaskRecord.threadRecords.size(); - for (ThreadRecord tr : mTaskRecord.threadRecords) { - long threadRect = tr.blockLen; - - File temp = new File(String.format(SUB_PATH, mTaskRecord.filePath, tr.threadId)); - if (!temp.exists()) { - ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath())); - tr.isComplete = false; - tr.startLocation = tr.threadId * normalRectLen; - } else { - if (!tr.isComplete) { - ALog.i(TAG, String.format( - "startLocation = %s; endLocation = %s; block = %s; tempLen = %s; threadId = %s", - tr.startLocation, tr.endLocation, threadRect, temp.length(), tr.threadId)); - - long blockFileLen = temp.length(); // 磁盘中的分块文件长度 - /* - * 检查磁盘中的分块文件 - */ - if (blockFileLen > threadRect) { - ALog.i(TAG, String.format("分块【%s】错误,分块长度【%s】 > 线程区间长度【%s】,将重新开始该分块", - tr.threadId, blockFileLen, threadRect)); - temp.delete(); - tr.startLocation = tr.threadId * threadRect; - continue; - } - - long realLocation = - tr.threadId * normalRectLen + blockFileLen; //正常情况下,该线程的startLocation的位置 - /* - * 检查记录文件 - */ - if (blockFileLen == threadRect) { - ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath())); - tr.startLocation = blockFileLen; - tr.isComplete = true; - } else if (tr.startLocation != realLocation) { // 处理记录小于分块文件长度的情况 - ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation)); - tr.startLocation = realLocation; - } - } else { - ALog.i(TAG, String.format("分块【%s】已完成", temp.getPath())); - } - } - } - mTaskWrapper.setNewTask(false); - } - - /** - * convertDb 是兼容性代码 从3.4.1开始,线程配置信息将存储在数据库中。 将配置文件的内容复制到数据库中,并将配置文件删除 - */ - private void convertDb() { - List records = - DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", - getFilePath()); - if (records == null || records.size() == 0) { - Properties pro = FileUtil.loadConfig(mConfigFile); - if (pro.isEmpty()) { - ALog.d(TAG, "老版本的线程记录为空,任务为新任务"); - initRecord(true); - return; - } - - Set keys = pro.keySet(); - // 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录... - // 第一步应该是record 和 state去重取正确的线程数 - Set set = new HashSet<>(); - for (Object key : keys) { - String str = String.valueOf(key); - int i = Integer.parseInt(str.substring(str.length() - 1)); - set.add(i); - } - int threadNum = set.size(); - if (threadNum == 0) { - ALog.d(TAG, "线程数为空,任务为新任务"); - initRecord(true); - return; - } - mTaskWrapper.setNewTask(false); - mTaskRecord = createTaskRecord(threadNum); - mTaskRecord.isOpenDynamicFile = false; - mTaskRecord.isBlock = false; - File tempFile = new File(getFilePath()); - for (int i = 0; i < threadNum; i++) { - ThreadRecord tRecord = new ThreadRecord(); - tRecord.taskKey = mTaskRecord.filePath; - Object state = pro.getProperty(tempFile.getName() + STATE + i); - Object record = pro.getProperty(tempFile.getName() + RECORD + i); - if (state != null && Integer.parseInt(String.valueOf(state)) == 1) { - tRecord.isComplete = true; - continue; - } - if (record != null) { - long temp = Long.parseLong(String.valueOf(record)); - tRecord.startLocation = temp > 0 ? temp : 0; - } else { - tRecord.startLocation = 0; - } - mTaskRecord.threadRecords.add(tRecord); - } - mConfigFile.delete(); - } - } - - /** - * 初始化任务记录,分配线程区间 - * - * @param newRecord {@code true} 需要创建新{@link TaskRecord} - */ - private void initRecord(boolean newRecord) { - if (newRecord) { - mTaskRecord = createTaskRecord(getNewTaskThreadNum()); - } - mTaskWrapper.setNewTask(true); - int requestType = mTaskWrapper.getRequestType(); - if (requestType == ITaskWrapper.M3U8_LIVE) { - return; - } - long blockSize = mEntity.getFileSize() / mTaskRecord.threadNum; - // 处理线程区间记录 - for (int i = 0; i < mTaskRecord.threadNum; i++) { - long startL = i * blockSize, endL = (i + 1) * blockSize; - ThreadRecord tr; - tr = new ThreadRecord(); - tr.taskKey = mTaskRecord.filePath; - tr.threadId = i; - tr.startLocation = startL; - tr.isComplete = false; - if (requestType == ITaskWrapper.M3U8_VOD) { - tr.startLocation = 0; - tr.threadType = TaskRecord.TYPE_M3U8_VOD; - tr.tsUrl = ((DTaskWrapper) mTaskWrapper).asM3U8().getUrls().get(i); - } else { - tr.threadType = TaskRecord.TYPE_HTTP_FTP; - //最后一个线程的结束位置即为文件的总长度 - if (i == (mTaskRecord.threadNum - 1)) { - endL = mEntity.getFileSize(); - } - tr.endLocation = endL; - tr.blockLen = RecordUtil.getBlockLen(mEntity.getFileSize(), i, mTaskRecord.threadNum); - } - mTaskRecord.threadRecords.add(tr); - } - } - - /** - * 创建任务记录 - * - * @param threadNum 线程总数 - */ - private TaskRecord createTaskRecord(int threadNum) { - TaskRecord record = new TaskRecord(); - record.fileName = mEntity.getFileName(); - record.filePath = getFilePath(); - record.threadRecords = new ArrayList<>(); - record.threadNum = threadNum; - int requestType = mTaskWrapper.getRequestType(); - if (requestType == ITaskWrapper.M3U8_VOD) { - record.taskType = TaskRecord.TYPE_M3U8_VOD; - record.isOpenDynamicFile = true; - record.bandWidth = ((DTaskWrapper)mTaskWrapper).asM3U8().getBandWidth(); - } else if (requestType == ITaskWrapper.M3U8_LIVE) { - record.taskType = TaskRecord.TYPE_M3U8_LIVE; - record.isOpenDynamicFile = true; - record.bandWidth = ((DTaskWrapper)mTaskWrapper).asM3U8().getBandWidth(); - } else { - if (getRecordType() == TYPE_DOWNLOAD) { - record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); - // 线程数为1,或者使用了分块,则认为是使用动态长度文件 - record.isOpenDynamicFile = threadNum == 1 || record.isBlock; - } else { - record.isBlock = false; - } - record.taskType = TaskRecord.TYPE_HTTP_FTP; - record.isGroupRecord = mEntity.isGroupChild(); - if (record.isGroupRecord) { - if (mEntity instanceof DownloadEntity) { - record.dGroupHash = ((DownloadEntity) mEntity).getGroupHash(); - } - } - } - - return record; - } - - /** - * 保存任务记录 - */ - private void saveRecord() { - mTaskRecord.threadNum = mTaskRecord.threadRecords.size(); - mTaskRecord.save(); - if (mTaskRecord.threadRecords != null && !mTaskRecord.threadRecords.isEmpty()) { - DbEntity.saveAll(mTaskRecord.threadRecords); - } - ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size())); - } - - /** - * 获取记录类型 - * - * @return {@link #TYPE_DOWNLOAD}、{@link #TYPE_UPLOAD} - */ - private int getRecordType() { - if (mEntity instanceof DownloadEntity) { - return TYPE_DOWNLOAD; - } else { - return TYPE_UPLOAD; - } - } - - /** - * 获取任务路径 - * - * @return 任务文件路径 - */ - private String getFilePath() { - if (mEntity instanceof DownloadEntity) { - return ((DownloadEntity) mTaskWrapper.getEntity()).getFilePath(); - } else { - return ((UploadEntity) mTaskWrapper.getEntity()).getFilePath(); - } - } - - /** - * 小于1m的文件或是任务组的子任务、线程数强制为1 - * 不支持断点或chunked模式的线程数都为,线程数强制为1 - */ - private int getNewTaskThreadNum() { - if (getRecordType() == TYPE_DOWNLOAD) { - if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { - return ((DTaskWrapper) mTaskWrapper).asM3U8().getUrls().size(); - } - if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { - return 1; - } - if (!mTaskWrapper.isSupportBP() || mTaskWrapper.asHttp().isChunked()) { - return 1; - } - int threadNum = Configuration.getInstance().downloadCfg.getThreadNum(); - return mEntity.getFileSize() <= SUB_LEN - || mEntity.isGroupChild() - || threadNum == 1 - ? 1 - : threadNum; - } else { - return 1; - } - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java index 78f102f2..bbf4db72 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java @@ -17,8 +17,8 @@ package com.arialyy.aria.core.common.controller; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.command.CmdHelper; /** * 创建任务时使用的控制器 @@ -37,7 +37,7 @@ public final class BuilderController extends FeatureController implements IStart public long add() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CREATE, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CREATE, checkTaskType())); return getEntity().getId(); } @@ -52,7 +52,7 @@ public final class BuilderController extends FeatureController implements IStart public long create() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, checkTaskType())); return getEntity().getId(); } @@ -63,7 +63,7 @@ public final class BuilderController extends FeatureController implements IStart @Override public long setHighestPriority() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_HIGHEST_PRIORITY, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_HIGHEST_PRIORITY, checkTaskType())); return getEntity().getId(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java index 6e385a19..d3bbca51 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java @@ -25,12 +25,12 @@ import com.arialyy.aria.core.download.CheckDGEntityUtil; import com.arialyy.aria.core.download.CheckFtpDirEntityUtil; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.ICheckEntityUtil; -import com.arialyy.aria.core.inf.ITask; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.upload.CheckUEntityUtil; import com.arialyy.aria.core.upload.UTaskWrapper; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java index 6cb27d12..9567d908 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java @@ -18,9 +18,9 @@ package com.arialyy.aria.core.common.controller; import com.arialyy.aria.core.command.CancelCmd; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.core.command.CmdHelper; /** * 启动控制器 @@ -39,7 +39,7 @@ public final class NormalController extends FeatureController implements INormal public void stop() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_STOP, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_STOP, checkTaskType())); } } @@ -51,7 +51,7 @@ public final class NormalController extends FeatureController implements INormal public void resume() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, checkTaskType())); } } @@ -63,7 +63,7 @@ public final class NormalController extends FeatureController implements INormal public void cancel() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, checkTaskType())); } } @@ -76,10 +76,10 @@ public final class NormalController extends FeatureController implements INormal if (checkConfig()) { int taskType = checkTaskType(); EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_STOP, taskType)); + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_STOP, taskType)); EventMsgUtil.getDefault() .post( - CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, taskType)); + CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, taskType)); } } @@ -93,7 +93,7 @@ public final class NormalController extends FeatureController implements INormal public void cancel(boolean removeFile) { if (checkConfig()) { CancelCmd cancelCmd = - (CancelCmd) CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, + (CancelCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, checkTaskType()); cancelCmd.removeFile = removeFile; EventMsgUtil.getDefault().post(cancelCmd); @@ -107,7 +107,7 @@ public final class NormalController extends FeatureController implements INormal public void reStart() { if (checkConfig()) { EventMsgUtil.getDefault() - .post(CommonUtil.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_RESTART, + .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_RESTART, checkTaskType())); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 360cec28..7bc96639 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -16,11 +16,11 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; import com.arialyy.aria.core.inf.ICheckEntityUtil; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.ITargetHandler; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; @@ -64,20 +64,24 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private void handleM3U8() { File file = new File(mWrapper.getTempFilePath()); + int bandWidth = (int) mWrapper.getM3U8Params().getParam(IOptionConstant.bandWidth); // 缓存文件夹格式:问文件夹/.文件名_码率 - String cacheDir = String.format("%s/.%s_%s", file.getParent(), file.getName(), - mWrapper.asM3U8().getBandWidth()); - mWrapper.asM3U8().setCacheDir(cacheDir); + String cacheDir = String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); + + mWrapper.getM3U8Params().setParams(IOptionConstant.cacheDir, cacheDir); M3U8Entity m3U8Entity = mEntity.getM3U8Entity(); + + Object temp = mWrapper.getM3U8Params().getParam(IOptionConstant.generateIndexFileTemp); + boolean generateIndexFileTemp = temp != null && (boolean) temp; if (m3U8Entity == null) { m3U8Entity = new M3U8Entity(); m3U8Entity.setFilePath(mEntity.getFilePath()); m3U8Entity.setPeerIndex(0); m3U8Entity.setCacheDir(cacheDir); - m3U8Entity.setGenerateIndexFile(mWrapper.asM3U8().isGenerateIndexFileTemp()); + m3U8Entity.setGenerateIndexFile(generateIndexFileTemp); m3U8Entity.insert(); } else { - m3U8Entity.setGenerateIndexFile(mWrapper.asM3U8().isGenerateIndexFileTemp()); + m3U8Entity.setGenerateIndexFile(generateIndexFileTemp); m3U8Entity.update(); } if (mWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { @@ -92,8 +96,8 @@ public class CheckDEntityUtil implements ICheckEntityUtil { } } - if (mWrapper.asM3U8().getBandWidthUrlConverter() != null - && mWrapper.asM3U8().getBandWidth() == 0) { + if (mWrapper.getM3U8Params().getHandler(IOptionConstant.bandWidthUrlConverter) != null + && bandWidth == 0) { ALog.w(TAG, "你已经设置了码率url转换器,但是没有设置码率,Aria框架将采用第一个获取到的码率"); } } @@ -140,7 +144,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { return false; } else { ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); - RecordUtil.delTaskRecord(filePath, RecordHandler.TYPE_DOWNLOAD); + RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD); } } @@ -149,7 +153,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { mEntity.setFileName(newFile.getName()); // 如过使用Content-Disposition中的文件名,将不会执行重命名工作 - if (mWrapper.asHttp().isUseServerFileName() + if ((boolean) mWrapper.getOptionParams().getParam(IOptionConstant.useServerFileName) || mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { return true; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 94d3fc8b..8a1b222a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.inf.ICheckEntityUtil; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -133,9 +134,10 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { return false; } - if (mWrapper.asHttp().getRequestEnum() == RequestEnum.POST) { - for (DTaskWrapper subTask : mWrapper.getSubTaskWrapper()) { - subTask.asHttp().setRequestEnum(RequestEnum.POST); + if (mWrapper.getOptionParams().getParam(IOptionConstant.requestEnum) + == RequestEnum.POST) { + for (DTaskWrapper subWrapper : mWrapper.getSubTaskWrapper()) { + subWrapper.getOptionParams().setParams(IOptionConstant.requestEnum, RequestEnum.POST); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java index 3f82a400..9db5605b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java @@ -16,7 +16,9 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; +import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.inf.ICheckEntityUtil; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import java.io.File; @@ -83,12 +85,15 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil { if (b) { mEntity.save(); } - if (mWrapper.asFtp().getUrlEntity().isFtps) { - if (TextUtils.isEmpty(mWrapper.asFtp().getUrlEntity().storePath)) { + FtpUrlEntity urlEntity = + (FtpUrlEntity) mWrapper.getOptionParams().getParam(IOptionConstant.ftpUrlEntity); + assert urlEntity != null; + if (urlEntity.isFtps) { + if (TextUtils.isEmpty(urlEntity.storePath)) { ALog.e(TAG, "证书路径为空"); return false; } - if (TextUtils.isEmpty(mWrapper.asFtp().getUrlEntity().keyAlias)) { + if (TextUtils.isEmpty(urlEntity.keyAlias)) { ALog.e(TAG, "证书别名为空"); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 71e5d906..fd7ab2ae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -33,15 +33,17 @@ import com.arialyy.aria.core.download.target.GroupNormalTarget; import com.arialyy.aria.core.download.target.HttpBuilderTarget; import com.arialyy.aria.core.download.target.HttpNormalTarget; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.ComponentUtil; import com.arialyy.aria.util.DbDataHelper; import java.util.ArrayList; import java.util.List; @@ -73,6 +75,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public HttpBuilderTarget load(@NonNull String url) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkUrlInvalidThrow(url); return DTargetFactory.getInstance() .generateBuilderTarget(HttpBuilderTarget.class, url); @@ -86,6 +89,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public HttpNormalTarget load(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(HttpNormalTarget.class, taskId); @@ -98,6 +102,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public GroupBuilderTarget loadGroup(List urls) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkDownloadUrls(urls); return DTargetFactory.getInstance().generateGroupBuilderTarget(urls); } @@ -110,6 +115,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public GroupNormalTarget loadGroup(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(GroupNormalTarget.class, taskId); @@ -120,6 +126,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public FtpBuilderTarget loadFtp(@NonNull String url) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkUrlInvalidThrow(url); return DTargetFactory.getInstance() .generateBuilderTarget(FtpBuilderTarget.class, url); @@ -133,6 +140,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public FtpNormalTarget loadFtp(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(FtpNormalTarget.class, taskId); @@ -143,6 +151,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public FtpDirBuilderTarget loadFtpDir(@NonNull String dirUrl) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkUrlInvalidThrow(dirUrl); return DTargetFactory.getInstance().generateDirBuilderTarget(dirUrl); } @@ -155,6 +164,7 @@ public class DownloadReceiver extends AbsReceiver { */ @CheckResult public FtpDirNormalTarget loadFtpDir(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(FtpDirNormalTarget.class, taskId); @@ -461,7 +471,7 @@ public class DownloadReceiver extends AbsReceiver { public void removeAllTask(boolean removeFile) { final AriaManager ariaManager = AriaManager.getInstance(); CancelAllCmd cancelCmd = - (CancelAllCmd) CommonUtil.createNormalCmd(new DTaskWrapper(null), + (CancelAllCmd) CmdHelper.createNormalCmd(new DTaskWrapper(null), NormalCmdFactory.TASK_CANCEL_ALL, ITask.DOWNLOAD); cancelCmd.removeFile = removeFile; EventMsgUtil.getDefault().post(cancelCmd); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java deleted file mode 100644 index 3fb04c6c..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/Downloader.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.download.downloader; - -import com.arialyy.aria.core.common.AbsThreadTask; -import com.arialyy.aria.core.common.NormalFileer; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.event.Event; -import com.arialyy.aria.core.event.SpeedEvent; -import com.arialyy.aria.core.inf.IDownloadListener; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.exception.TaskException; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.BufferedRandomAccessFile; -import java.io.File; -import java.io.IOException; - -/** - * Created by AriaL on 2017/7/1. 文件下载器 - */ -public class Downloader extends NormalFileer { - private String TAG = "Downloader"; - - public Downloader(IDownloadListener listener, DTaskWrapper taskWrapper) { - super(listener, taskWrapper); - mTempFile = new File(mEntity.getFilePath()); - setUpdateInterval(taskWrapper.getConfig().getUpdateInterval()); - } - - @Override protected boolean handleNewTask() { - if (!mRecord.isBlock) { - if (mTempFile.exists()) { - mTempFile.delete(); - } - //CommonUtil.createFile(mTempFile.getPath()); - } else { - for (int i = 0; i < mTotalThreadNum; i++) { - File blockFile = new File(String.format(RecordHandler.SUB_PATH, mTempFile.getPath(), i)); - if (blockFile.exists()) { - ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); - blockFile.delete(); - } - } - } - BufferedRandomAccessFile file = null; - try { - if (mTotalThreadNum > 1 && !mRecord.isBlock) { - file = new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192); - //设置文件长度 - file.setLength(mEntity.getFileSize()); - } - return true; - } catch (IOException e) { - failDownload(new TaskException(TAG, - String.format("下载失败,filePath: %s, url: %s", mEntity.getDownloadPath(), - mEntity.getUrl()), e)); - } finally { - if (file != null) { - try { - file.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return false; - } - - /** - * 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径 - */ - void updateTempFile() { - if (!mTempFile.getPath().equals(mEntity.getFilePath())) { - if (!mTempFile.exists()) { - mTempFile = new File(mEntity.getFilePath()); - } else { - boolean b = mTempFile.renameTo(new File(mEntity.getDownloadPath())); - ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败")); - } - } - } - - @Event - public void setMaxSpeed(SpeedEvent event) { - setMaxSpeed(event.speed); - } - - @Override protected void onPostPre() { - super.onPostPre(); - ((IDownloadListener) mListener).onPostPre(mEntity.getFileSize()); - File file = new File(mEntity.getDownloadPath()); - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } - } - - @Override protected AbsThreadTask selectThreadTask(SubThreadConfig config) { - switch (mTaskWrapper.getRequestType()) { - case ITaskWrapper.D_FTP: - case ITaskWrapper.D_FTP_DIR: - return new FtpThreadTask(config); - case ITaskWrapper.D_HTTP: - return new HttpThreadTask(config); - } - return null; - } - - private void failDownload(BaseException e) { - closeTimer(); - mListener.onFail(false, e); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java deleted file mode 100644 index ad28bb41..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/SimpleDownloadUtil.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.arialyy.aria.core.download.downloader; - -import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.IDownloadListener; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.exception.BaseException; - -/** - * Created by lyy on 2015/8/25. - * D_HTTP\FTP单任务下载工具 - */ -public class SimpleDownloadUtil implements IUtil { - private String TAG = "SimpleDownloadUtil"; - private IDownloadListener mListener; - private Downloader mDownloader; - private DTaskWrapper mTaskWrapper; - private boolean isStop = false, isCancel = false; - - public SimpleDownloadUtil(DTaskWrapper wrapper, IDownloadListener downloadListener) { - mTaskWrapper = wrapper; - mListener = downloadListener; - mDownloader = new Downloader(downloadListener, wrapper); - } - - @Override public String getKey() { - return mTaskWrapper.getKey(); - } - - @Override public long getFileSize() { - return mDownloader.getFileSize(); - } - - /** - * 获取当前下载位置 - */ - @Override public long getCurrentLocation() { - return mDownloader.getCurrentLocation(); - } - - @Override public boolean isRunning() { - return mDownloader.isRunning(); - } - - /** - * 取消下载 - */ - @Override public void cancel() { - isCancel = true; - mDownloader.cancel(); - } - - /** - * 停止下载 - */ - @Override public void stop() { - isStop = true; - mDownloader.stop(); - } - - /** - * 多线程断点续传下载文件,开始下载 - */ - @Override public void start() { - if (isStop || isCancel) { - return; - } - mListener.onPre(); - // 如果网址没有变,而服务器端端文件改变,以下代码就没有用了 - //if (mTaskWrapper.getEntity().getFileSize() <= 1 - // || mTaskWrapper.isRefreshInfo() - // || mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP - // || mTaskWrapper.getState() == IEntity.STATE_FAIL) { - // new Thread(createInfoThread()).create(); - //} else { - // mDownloader.create(); - //} - new Thread(createInfoThread()).start(); - } - - private void failDownload(BaseException e, boolean needRetry) { - if (isStop || isCancel) { - return; - } - mListener.onFail(needRetry, e); - mDownloader.onDestroy(); - } - - /** - * 通过链接类型创建不同的获取文件信息的线程 - */ - private Runnable createInfoThread() { - switch (mTaskWrapper.getRequestType()) { - case ITaskWrapper.D_FTP: - return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String url, CompleteInfo info) { - mDownloader.updateTempFile(); - mDownloader.start(); - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failDownload(e, needRetry); - mDownloader.closeTimer(); - } - }); - case ITaskWrapper.D_HTTP: - return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String url, CompleteInfo info) { - mDownloader.updateTempFile(); - mDownloader.start(); - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failDownload(e, needRetry); - mDownloader.closeTimer(); - } - }); - } - return null; - } -} \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDLoadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDLoadUtil.java deleted file mode 100644 index ee9713b8..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/SubDLoadUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.download.group; - -import android.os.Handler; -import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.downloader.Downloader; -import com.arialyy.aria.core.download.downloader.HttpFileInfoThread; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.core.scheduler.ISchedulers; -import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.util.ALog; - -/** - * 子任务下载器,负责创建{@link Downloader} - */ -class SubDLoadUtil implements IUtil { - private final String TAG = "SubDownloadLoader"; - - private Downloader mDownloader; - private DTaskWrapper mWrapper; - private Handler mSchedulers; - private ChildDLoadListener mListener; - private boolean needGetInfo; - - /** - * @param schedulers 调度器 - * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 - */ - SubDLoadUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) { - mWrapper = taskWrapper; - mSchedulers = schedulers; - this.needGetInfo = needGetInfo; - mListener = new ChildDLoadListener(mSchedulers, SubDLoadUtil.this); - } - - @Override public String getKey() { - return mWrapper.getKey(); - } - - public DTaskWrapper getWrapper() { - return mWrapper; - } - - public DownloadEntity getEntity() { - return mWrapper.getEntity(); - } - - /** - * 重新开始任务 - */ - void reStart() { - if (mDownloader != null) { - mDownloader.retryTask(); - } - } - - public Downloader getDownloader() { - return mDownloader; - } - - @Override public long getFileSize() { - return mDownloader == null ? -1 : mDownloader.getFileSize(); - } - - @Override public long getCurrentLocation() { - return mDownloader == null ? -1 : mDownloader.getCurrentLocation(); - } - - @Override public boolean isRunning() { - return mDownloader != null && mDownloader.isRunning(); - } - - @Override public void cancel() { - if (mDownloader != null && isRunning()) { - mDownloader.cancel(); - } else { - mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); - } - } - - @Override public void stop() { - if (mDownloader != null && isRunning()) { - mDownloader.stop(); - } else { - mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget(); - } - } - - @Override public void start() { - if (mWrapper.getRequestType() == ITaskWrapper.D_HTTP) { - if (needGetInfo) { - new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() { - - @Override public void onComplete(String url, CompleteInfo info) { - mDownloader = new Downloader(mListener, mWrapper); - mDownloader.start(); - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - mSchedulers.obtainMessage(ISchedulers.FAIL, SubDLoadUtil.this).sendToTarget(); - } - })).start(); - } else { - mDownloader = new Downloader(mListener, mWrapper); - mDownloader.start(); - } - } else if (mWrapper.getRequestType() == ITaskWrapper.D_FTP) { - mDownloader = new Downloader(mListener, mWrapper); - mDownloader.start(); - } else { - ALog.w(TAG, String.format("不识别的类型,requestType:%s", mWrapper.getRequestType())); - } - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java index beea90f8..f498d3cb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java @@ -17,10 +17,15 @@ package com.arialyy.aria.core.download.m3u8; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.Suggest; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ComponentUtil; /** * m3u8 委托 @@ -30,6 +35,7 @@ public class M3U8Delegate extends BaseDelegate public M3U8Delegate(TARGET target, AbsTaskWrapper wrapper) { super(target, wrapper); + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8); mTaskWrapper = (DTaskWrapper) getTaskWrapper(); mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_VOD); } @@ -40,7 +46,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate generateIndexFile() { - mTaskWrapper.asM3U8().setGenerateIndexFileTemp(true); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.generateIndexFileTemp, true); return this; } @@ -51,7 +57,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate merge(boolean merge) { - mTaskWrapper.asM3U8().setMergeFile(merge); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.mergeFile, merge); return this; } @@ -61,7 +67,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setMergeHandler(ITsMergeHandler handler) { - mTaskWrapper.asM3U8().setMergeHandler(handler); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.mergeHandler, handler); return this; } @@ -73,7 +79,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setTsUrlConvert(IVodTsUrlConverter converter) { - mTaskWrapper.asM3U8().setVodUrlConverter(converter); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.vodUrlConverter, converter); return this; } @@ -84,7 +90,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setBandWidth(int bandWidth) { - mTaskWrapper.asM3U8().setBandWidth(bandWidth); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.bandWidth, bandWidth); return this; } @@ -96,7 +102,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setBandWidthUrlConverter(IBandWidthUrlConverter converter) { - mTaskWrapper.asM3U8().setBandWidthUrlConverter(converter); + mTaskWrapper.getM3U8Params().setParams(IOptionConstant.bandWidthUrlConverter, converter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java index edf7cf36..353ce357 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java @@ -17,10 +17,12 @@ package com.arialyy.aria.core.download.m3u8; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.util.ALog; /** @@ -41,7 +43,8 @@ public class M3U8LiveDelegate extends BaseDelegate setLiveTsUrlConvert(ILiveTsUrlConverter converter) { - ((DTaskWrapper) getTaskWrapper()).asM3U8().setLiveTsUrlConverter(converter); + ((DTaskWrapper) getTaskWrapper()).getM3U8Params() + .setParams(IOptionConstant.liveTsUrlConverter, converter); return this; } @@ -56,7 +59,8 @@ public class M3U8LiveDelegate extends BaseDelegate extends BaseDelegate extends BaseDelegate extends BaseDelegate mUrls = new ArrayList<>(); - private M3U8VodLoader mLoader; - - public M3U8VodUtil(DTaskWrapper wrapper, M3U8Listener listener) { - mWrapper = wrapper; - mListener = listener; - mLoader = new M3U8VodLoader(mListener, mWrapper); - } - - @Override public String getKey() { - return mWrapper.getKey(); - } - - @Override public long getFileSize() { - return 0; - } - - @Override public long getCurrentLocation() { - return 0; - } - - @Override public boolean isRunning() { - return mLoader.isRunning(); - } - - @Override public void cancel() { - isCancel = true; - mLoader.cancel(); - } - - @Override public void stop() { - isStop = true; - mLoader.stop(); - } - - @Override public void start() { - if (isStop || isCancel) { - return; - } - mListener.onPre(); - // peer数量小于0, - M3U8Entity m3U8Entity = mWrapper.getEntity().getM3U8Entity(); - if (m3U8Entity.getPeerNum() <= 0 || (m3U8Entity.isGenerateIndexFile() && !new File( - String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, - mWrapper.getEntity().getFilePath())).exists())) { - getVodInfo(); - } else { - mLoader.start(); - } - } - - /** - * 获取点播文件信息 - */ - private void getVodInfo() { - M3U8InfoThread thread = new M3U8InfoThread(mWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String key, CompleteInfo info) { - IVodTsUrlConverter converter = mWrapper.asM3U8().getVodUrlConverter(); - if (converter != null) { - if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) { - mUrls.addAll(converter.convert(mWrapper.getEntity().getUrl(), (List) info.obj)); - } else { - mUrls.addAll( - converter.convert(mWrapper.asM3U8().getBandWidthUrl(), (List) info.obj)); - } - } else { - mUrls.addAll((Collection) info.obj); - } - if (mUrls.isEmpty()) { - failDownload(new M3U8Exception(TAG, "获取地址失败"), false); - return; - } else if (!mUrls.get(0).startsWith("http")) { - failDownload(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); - return; - } - mWrapper.asM3U8().setUrls(mUrls); - if (isStop) { - mListener.onStop(mWrapper.getEntity().getCurrentProgress()); - } else if (isCancel) { - mListener.onCancel(); - } else { - mLoader.start(); - } - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failDownload(e, needRetry); - } - }); - new Thread(thread).start(); - } - - private void failDownload(BaseException e, boolean needRetry) { - if (isStop || isCancel) { - return; - } - mListener.onFail(needRetry, e); - mLoader.onDestroy(); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java index d3eae6e8..e0fa063b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java @@ -19,13 +19,13 @@ import android.text.TextUtils; import androidx.annotation.CheckResult; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IConfigHandler; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CommonUtil; @@ -77,7 +77,7 @@ abstract class AbsGroupConfigHandler implements IConfi } @Override public boolean isRunning() { - DownloadGroupTask task = DownloadGroupTaskQueue.getInstance().getTask(getEntity().getKey()); + DownloadGroupTask task = DGroupTaskQueue.getInstance().getTask(getEntity().getKey()); return task != null && task.isRunning(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java index 5eeb8aea..32343bc8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java @@ -22,7 +22,7 @@ import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IConfigHandler; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; @@ -83,7 +83,7 @@ class DNormalConfigHandler implements IConfigHandler { } @Override public boolean isRunning() { - return DownloadTaskQueue.getInstance().taskIsRunning(mEntity.getKey()); + return DTaskQueue.getInstance().taskIsRunning(mEntity.getKey()); } void setForceDownload(boolean forceDownload) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 55c44716..f0ed90dc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -18,10 +18,11 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.FtpDelegate; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; /** @@ -33,7 +34,8 @@ public class FtpBuilderTarget extends AbsBuilderTarget { FtpBuilderTarget(String url) { mConfigHandler = new DNormalConfigHandler<>(this, -1); mConfigHandler.setUrl(url); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(url)); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(url)); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index a2e97691..421756f7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -17,9 +17,10 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.FtpDelegate; import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -33,7 +34,8 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { FtpDirBuilderTarget(String url) { mConfigHandler = new FtpDirConfigHandler<>(this, -1); getEntity().setGroupHash(url); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(url)); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(url)); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java index 35720be0..f731e41b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java @@ -16,10 +16,9 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.target.AbsGroupConfigHandler; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.util.List; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index 742f455f..baff2e01 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -17,9 +17,10 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.FtpDelegate; import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -32,7 +33,8 @@ public class FtpDirNormalTarget extends AbsNormalTarget { FtpDirNormalTarget(long taskId) { mConfigHandler = new FtpDirConfigHandler<>(this, taskId); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getKey())); } @Override public boolean isRunning() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index 8c912f06..c7e48d0d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -18,10 +18,11 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.FtpDelegate; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; /** @@ -33,7 +34,8 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new DNormalConfigHandler<>(this, taskId); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index 813561d8..b44f37a4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -17,12 +17,13 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.SubTaskManager; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import java.util.List; @@ -150,7 +151,7 @@ public class GroupBuilderTarget extends AbsBuilderTarget { if (adapter == null) { throw new IllegalArgumentException("adapter为空"); } - getTaskWrapper().asHttp().setFileLenAdapter(adapter); + getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 2e7855b1..e70d2143 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -17,9 +17,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.HttpDelegate; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.manager.SubTaskManager; import java.util.List; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index e5eb6d5b..90112482 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -18,11 +18,12 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; +import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.download.m3u8.M3U8Delegate; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.wrapper.ITaskWrapper; public class HttpBuilderTarget extends AbsBuilderTarget { @@ -54,7 +55,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget useServerFileName(boolean use) { - getTaskWrapper().asHttp().setUseServerFileName(use); + getTaskWrapper().getOptionParams().setParams(IOptionConstant.useServerFileName, use); return this; } @@ -94,7 +95,8 @@ public class HttpBuilderTarget extends AbsBuilderTarget { if (adapter == null) { throw new IllegalArgumentException("adapter为空"); } - getTaskWrapper().asHttp().setFileLenAdapter(adapter); + + getTaskWrapper().getOptionParams().setParams(IOptionConstant.fileLenAdapter, adapter); return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java index 5ac49fb1..8b8f1d3d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.target.AbsGroupConfigHandler; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java index ada14aff..aed1f7ba 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java @@ -17,8 +17,8 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.m3u8.M3U8Delegate; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java index 64b6041a..bd2918fe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -18,9 +18,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.download.tcp.TcpDelegate; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; /** * @Author aria diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java index b8d395a5..6bbc83c0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java @@ -18,10 +18,9 @@ package com.arialyy.aria.core.download.tcp; import android.text.TextUtils; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import java.nio.charset.Charset; @@ -36,7 +35,7 @@ public class TcpDelegate extends BaseDelegate public TcpDelegate(TARGET target, AbsTaskWrapper wrapper) { super(target, wrapper); - mTcpConfig = ((DTaskWrapper) wrapper).asTcp(); + mTcpConfig = (TcpTaskConfig) wrapper.getTaskOption(); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java deleted file mode 100644 index d3fcb898..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalTask.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.inf; - -/** - * Created by lyy on 2017/6/3. - */ -public abstract class AbsNormalTask - extends AbsTask { - - /** - * 最高优先级命令,最高优先级命令有以下属性 - * 1、在下载队列中,有且只有一个最高优先级任务 - * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 - * 3、任务调度器不会暂停最高优先级任务 - * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 - * 5、如果下载队列中已经满了,则会停止队尾的任务 - * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 - */ - public void setHighestPriority(boolean isHighestPriority) { - isHeighestTask = isHighestPriority; - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java index 5d8fefed..95af5111 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java @@ -16,9 +16,9 @@ package com.arialyy.aria.core.inf; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -85,7 +85,7 @@ public abstract class AbsReceiver implements IReceiver { } /** - * 移除{@link DownloadTaskQueue}、{@link DownloadGroupTaskQueue}、{@link UploadTaskQueue}中注册的观察者 + * 移除{@link DTaskQueue}、{@link DGroupTaskQueue}、{@link UTaskQueue}中注册的观察者 */ protected abstract void unRegisterListener(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 3c190bf0..3fc67aa2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -17,7 +17,8 @@ package com.arialyy.aria.core.inf; import android.text.TextUtils; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.Suggest; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.common.controller.BuilderController; import com.arialyy.aria.core.common.controller.NormalController; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IConfigHandler.java index 445b23f5..98ede7ef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IConfigHandler.java @@ -15,6 +15,8 @@ */ package com.arialyy.aria.core.inf; +import com.arialyy.aria.core.common.AbsEntity; + /** * Created by lyy on 2019/4/5. * 普通任务配置处理 diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java index 02bca7ff..91f72f25 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java @@ -15,11 +15,11 @@ */ package com.arialyy.aria.core.manager; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.common.TaskRecord; +import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IRecordHandler; import java.io.File; /** @@ -75,7 +75,7 @@ class DTaskWrapperFactory implements INormalTEFactory= 1) { for (int i = 0; i < diff; i++) { TASK nextTask = getNextTask(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java similarity index 75% rename from Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java rename to Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java index 9dbfbc70..ce59202b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadGroupTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java @@ -18,35 +18,44 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.event.DGMaxNumEvent; +import com.arialyy.aria.core.event.Event; +import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.util.ALog; /** * Created by AriaL on 2017/6/29. 任务组下载队列 */ -public class DownloadGroupTaskQueue +public class DGroupTaskQueue extends AbsTaskQueue { - private static volatile DownloadGroupTaskQueue INSTANCE = null; + private static volatile DGroupTaskQueue INSTANCE = null; private final String TAG = "DownloadGroupTaskQueue"; - public static DownloadGroupTaskQueue getInstance() { + public static DGroupTaskQueue getInstance() { if (INSTANCE == null) { - synchronized (DownloadGroupTaskQueue.class) { - INSTANCE = new DownloadGroupTaskQueue(); + synchronized (DGroupTaskQueue.class) { + INSTANCE = new DGroupTaskQueue(); + EventMsgUtil.getDefault().register(INSTANCE); } } return INSTANCE; } - private DownloadGroupTaskQueue() { + private DGroupTaskQueue() { } @Override int getQueueType() { return TYPE_DG_QUEUE; } + @Event + public void maxTaskNum(DGMaxNumEvent event) { + setMaxTaskNum(event.maxNum); + } + @Override public int getMaxTaskNum() { return AriaManager.getInstance().getDGroupConfig().getMaxTaskNum(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java similarity index 85% rename from Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java rename to Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java index 8b7d7589..eb552fad 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DownloadTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java @@ -18,7 +18,10 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.event.DMaxNumEvent; +import com.arialyy.aria.core.event.Event; +import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.util.ALog; @@ -30,26 +33,32 @@ import java.util.Set; * Created by lyy on 2016/8/17. * 下载任务队列 */ -public class DownloadTaskQueue extends AbsTaskQueue { +public class DTaskQueue extends AbsTaskQueue { private static final String TAG = "DownloadTaskQueue"; - private static volatile DownloadTaskQueue INSTANCE = null; + private static volatile DTaskQueue INSTANCE = null; - public static DownloadTaskQueue getInstance() { + public static DTaskQueue getInstance() { if (INSTANCE == null) { - synchronized (DownloadTaskQueue.class) { - INSTANCE = new DownloadTaskQueue(); + synchronized (DTaskQueue.class) { + INSTANCE = new DTaskQueue(); + EventMsgUtil.getDefault().register(INSTANCE); } } return INSTANCE; } - private DownloadTaskQueue() { + private DTaskQueue() { } @Override int getQueueType() { return TYPE_D_QUEUE; } + @Event + public void maxTaskNum(DMaxNumEvent event) { + setMaxTaskNum(event.maxNum); + } + @Override public int getOldMaxNum() { return AriaManager.getInstance().getDownloadConfig().oldMaxTaskNum; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java index b669150b..e25c0813 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/ITaskQueue.java @@ -17,13 +17,12 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITask; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.upload.UTaskWrapper; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/TaskFactory.java b/Aria/src/main/java/com/arialyy/aria/core/queue/TaskFactory.java index 80a38001..eebde726 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/TaskFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/TaskFactory.java @@ -18,13 +18,13 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITask; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; /** * Created by lyy on 2016/8/18. diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java similarity index 74% rename from Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java rename to Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java index a6b17733..026096ec 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/UploadTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java @@ -17,28 +17,37 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.event.Event; +import com.arialyy.aria.core.event.EventMsgUtil; +import com.arialyy.aria.core.event.UMaxNumEvent; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.util.ALog; /** * Created by lyy on 2017/2/27. 上传任务队列 */ -public class UploadTaskQueue extends AbsTaskQueue { +public class UTaskQueue extends AbsTaskQueue { private static final String TAG = "UploadTaskQueue"; - private static volatile UploadTaskQueue INSTANCE = null; + private static volatile UTaskQueue INSTANCE = null; - public static UploadTaskQueue getInstance() { + public static UTaskQueue getInstance() { if (INSTANCE == null) { - synchronized (UploadTaskQueue.class) { - INSTANCE = new UploadTaskQueue(); + synchronized (UTaskQueue.class) { + INSTANCE = new UTaskQueue(); + EventMsgUtil.getDefault().register(INSTANCE); } } return INSTANCE; } - private UploadTaskQueue() { + private UTaskQueue() { + } + + @Event + public void maxTaskNum(UMaxNumEvent event){ + setMaxTaskNum(event.maxNum); } @Override int getQueueType() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java index 5a5a96de..2fd7f409 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.queue.pool; import android.text.TextUtils; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.LinkedHashSet; diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java index 03fe9e49..4ae49c99 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java @@ -18,7 +18,7 @@ package com.arialyy.aria.core.queue.pool; import android.text.TextUtils; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.Map; diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java index f9e64b0a..5a494822 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.queue.pool; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.task.AbsTask; /** * Created by AriaL on 2017/6/29. diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java index f43c2bb2..be863d4e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.queue.pool; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.Set; diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java index 313520cb..d016c81f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/IPool.java @@ -16,8 +16,8 @@ package com.arialyy.aria.core.queue.pool; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.task.AbsTask; /** * Created by lyy on 2016/8/14. 任务池 diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java index 7988fc5b..ae31ef4b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.queue.pool; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsTask; +import com.arialyy.aria.core.task.AbsTask; /** * Created by Aria.Lao on 2017/7/17. diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java index 18c352f2..3b5873e1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java @@ -16,8 +16,8 @@ package com.arialyy.aria.core.scheduler; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.ITaskQueue; import com.arialyy.aria.util.ALog; diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/NormalTaskListener.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/NormalTaskListener.java index 08b1a8bf..4757ab9a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/NormalTaskListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/NormalTaskListener.java @@ -15,10 +15,10 @@ */ package com.arialyy.aria.core.scheduler; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.ITask; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.task.UploadTask; /** * Created by Aria.Lao on 2017/6/7. diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/SubTaskListener.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/SubTaskListener.java index d29fea62..036b1185 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/SubTaskListener.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/SubTaskListener.java @@ -15,8 +15,8 @@ */ package com.arialyy.aria.core.scheduler; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.task.ITask; /** * Created by Aria.Lao on 2019/6/26. diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java index 33f8c7eb..771040c5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java @@ -20,21 +20,22 @@ import android.os.Bundle; import android.os.Message; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.GroupSendParams; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; -import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.queue.ITaskQueue; -import com.arialyy.aria.core.queue.UploadTaskQueue; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.NetUtils; import java.util.Iterator; @@ -78,13 +79,13 @@ public class TaskSchedulers implements ISchedulers { ITaskQueue getQueue(TASK curTask) { int taskType = curTask.getTaskType(); if (taskType == ITask.DOWNLOAD) { - return DownloadTaskQueue.getInstance(); + return DTaskQueue.getInstance(); } if (taskType == ITask.DOWNLOAD_GROUP) { - return DownloadGroupTaskQueue.getInstance(); + return DGroupTaskQueue.getInstance(); } if (taskType == ITask.UPLOAD) { - return UploadTaskQueue.getInstance(); + return UTaskQueue.getInstance(); } throw new NullPointerException("任务类型错误,type = " + taskType); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 0424fa47..50995ee0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -21,15 +21,16 @@ import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.CancelAllCmd; +import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.common.AbsBuilderTarget; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.inf.ITask; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.scheduler.TaskSchedulers; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.upload.target.FtpBuilderTarget; import com.arialyy.aria.core.upload.target.FtpNormalTarget; import com.arialyy.aria.core.upload.target.HttpBuilderTarget; @@ -38,7 +39,7 @@ import com.arialyy.aria.core.upload.target.UTargetFactory; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.ComponentUtil; import java.util.List; import java.util.Set; @@ -68,6 +69,7 @@ public class UploadReceiver extends AbsReceiver { */ @CheckResult public HttpBuilderTarget load(@NonNull String filePath) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkUploadPath(filePath); return UTargetFactory.getInstance() .generateBuilderTarget(HttpBuilderTarget.class, filePath); @@ -81,6 +83,7 @@ public class UploadReceiver extends AbsReceiver { */ @CheckResult public HttpNormalTarget load(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkTaskId(taskId); return UTargetFactory.getInstance() .generateNormalTarget(HttpNormalTarget.class, taskId); @@ -93,6 +96,7 @@ public class UploadReceiver extends AbsReceiver { */ @CheckResult public FtpBuilderTarget loadFtp(@NonNull String filePath) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkUploadPath(filePath); return UTargetFactory.getInstance() .generateBuilderTarget(FtpBuilderTarget.class, filePath); @@ -106,6 +110,7 @@ public class UploadReceiver extends AbsReceiver { */ @CheckResult public FtpNormalTarget loadFtp(long taskId) { + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkTaskId(taskId); return UTargetFactory.getInstance() .generateNormalTarget(FtpNormalTarget.class, taskId); @@ -237,7 +242,7 @@ public class UploadReceiver extends AbsReceiver { public void removeAllTask(boolean removeFile) { final AriaManager am = AriaManager.getInstance(); CancelAllCmd cancelCmd = - (CancelAllCmd) CommonUtil.createNormalCmd(new UTaskWrapper(null), + (CancelAllCmd) CmdHelper.createNormalCmd(new UTaskWrapper(null), NormalCmdFactory.TASK_CANCEL_ALL, ITask.UPLOAD); cancelCmd.removeFile = removeFile; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index a23b04a2..5dd3d981 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -17,11 +17,11 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; -import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by Aria.Lao on 2017/7/27. diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index e2db2cc4..994b3004 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -17,9 +17,10 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.ftp.FtpDelegate; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.util.CommonUtil; @@ -32,7 +33,9 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new UNormalConfigHandler<>(this, taskId); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 53682c4e..89ede1dd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -17,9 +17,9 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.HttpDelegate; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by lyy on 2017/2/28. diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index 95bc0c94..8edf5eb5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -17,9 +17,9 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.Suggest; -import com.arialyy.aria.core.common.http.HttpDelegate; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.HttpDelegate; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by lyy on 2017/2/28. diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java index 9ccf2faa..251e628c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java @@ -15,16 +15,17 @@ */ package com.arialyy.aria.core.upload.target; -import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.event.ErrorEvent; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IConfigHandler; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CommonUtil; import java.io.File; @@ -65,7 +66,8 @@ class UNormalConfigHandler implements IConfigHandler { if (uploadInterceptor == null) { throw new NullPointerException("ftp拦截器为空"); } - getTaskWrapper().asFtp().setUploadInterceptor(uploadInterceptor); + getTaskWrapper().getOptionParams() + .setObjs(IOptionConstant.uploadInterceptor, uploadInterceptor); return mTarget; } @@ -78,13 +80,14 @@ class UNormalConfigHandler implements IConfigHandler { } @Override public boolean isRunning() { - UploadTask task = UploadTaskQueue.getInstance().getTask(mEntity.getKey()); + UploadTask task = UTaskQueue.getInstance().getTask(mEntity.getKey()); return task != null && task.isRunning(); } void setTempUrl(String tempUrl) { getTaskWrapper().setTempUrl(tempUrl); - getTaskWrapper().asFtp().setUrlEntity(CommonUtil.getFtpUrlInfo(tempUrl)); + getTaskWrapper().getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(tempUrl)); } private UTaskWrapper getTaskWrapper() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java deleted file mode 100644 index 528da2d4..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/SimpleUploadUtil.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.upload.uploader; - -import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IUploadListener; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.util.CheckUtil; - -/** - * Created by lyy on 2017/2/9. - * 简单的文件上传工具 - */ -public class SimpleUploadUtil implements IUtil, Runnable { - private static final String TAG = "SimpleUploadUtil"; - - private UTaskWrapper mTaskWrapper; - private IUploadListener mListener; - private Uploader mUploader; - private boolean isStop = false, isCancel = false; - - public SimpleUploadUtil(UTaskWrapper taskWrapper, IUploadListener listener) { - mTaskWrapper = taskWrapper; - CheckUtil.checkTaskEntity(taskWrapper); - if (listener == null) { - throw new IllegalArgumentException("上传监听不能为空"); - } - mListener = listener; - mUploader = new Uploader(mListener, taskWrapper); - } - - @Override public void run() { - mListener.onPre(); - switch (mTaskWrapper.getRequestType()) { - case AbsTaskWrapper.U_FTP: - FtpFileInfoThread infoThread = - new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String url, CompleteInfo info) { - if (info.code == FtpFileInfoThread.CODE_COMPLETE) { - mListener.onComplete(); - } else { - mUploader.start(); - } - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failUpload(e, needRetry); - } - }); - new Thread(infoThread).start(); - break; - case AbsTaskWrapper.U_HTTP: - mUploader.start(); - break; - } - } - - private void failUpload(BaseException e, boolean needRetry) { - if (isStop || isCancel) { - return; - } - mListener.onFail(needRetry, e); - mUploader.onDestroy(); - } - - @Override public String getKey() { - return mTaskWrapper.getKey(); - } - - @Override public long getFileSize() { - return mUploader.getFileSize(); - } - - @Override public long getCurrentLocation() { - return mUploader.getCurrentLocation(); - } - - @Override public boolean isRunning() { - return mUploader.isRunning(); - } - - @Override public void cancel() { - isCancel = true; - mUploader.cancel(); - } - - @Override public void stop() { - isStop = true; - mUploader.stop(); - } - - @Override public void start() { - if (isStop || isCancel) { - return; - } - new Thread(this).start(); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java b/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java deleted file mode 100644 index 32858fce..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/Uploader.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.upload.uploader; - -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.AbsThreadTask; -import com.arialyy.aria.core.common.NormalFileer; -import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.event.Event; -import com.arialyy.aria.core.event.SpeedEvent; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IUploadListener; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; -import java.io.File; - -/** - * Created by Aria.Lao on 2017/7/27. - * 文件上传器 - */ -class Uploader extends NormalFileer { - - Uploader(IUploadListener listener, UTaskWrapper taskEntity) { - super(listener, taskEntity); - mTempFile = new File(mEntity.getFilePath()); - setUpdateInterval( - AriaManager.getInstance().getUploadConfig().getUpdateInterval()); - } - - @Override protected boolean handleNewTask() { - return true; - } - - @Override protected AbsThreadTask selectThreadTask(SubThreadConfig config) { - switch (mTaskWrapper.getRequestType()) { - case AbsTaskWrapper.U_FTP: - return new FtpThreadTask(config); - case AbsTaskWrapper.U_HTTP: - return new HttpThreadTask(config); - } - return null; - } - - @Event - public void setMaxSpeed(SpeedEvent event) { - setMaxSpeed(event.speed); - } -} diff --git a/AriaAnnotations/src/main/java/com/arialyy/annotations/TaskEnum.java b/AriaAnnotations/src/main/java/com/arialyy/annotations/TaskEnum.java index 5723fad1..29cf5163 100644 --- a/AriaAnnotations/src/main/java/com/arialyy/annotations/TaskEnum.java +++ b/AriaAnnotations/src/main/java/com/arialyy/annotations/TaskEnum.java @@ -20,15 +20,15 @@ package com.arialyy.annotations; * 任务类型枚举 */ public enum TaskEnum { - DOWNLOAD("com.arialyy.aria.core.download", "DownloadTask", "$$DownloadListenerProxy", + DOWNLOAD("com.arialyy.aria.core.task", "DownloadTask", "$$DownloadListenerProxy", "NormalTaskListener"), - DOWNLOAD_GROUP("com.arialyy.aria.core.download", "DownloadGroupTask", + DOWNLOAD_GROUP("com.arialyy.aria.core.task", "DownloadGroupTask", "$$DownloadGroupListenerProxy", "NormalTaskListener"), - DOWNLOAD_GROUP_SUB("com.arialyy.aria.core.download", "DownloadGroupTask", + DOWNLOAD_GROUP_SUB("com.arialyy.aria.core.task", "DownloadGroupTask", "$$DGSubListenerProxy", "SubTaskListener"), - UPLOAD("com.arialyy.aria.core.upload", "UploadTask", "$$UploadListenerProxy", + UPLOAD("com.arialyy.aria.core.task", "UploadTask", "$$UploadListenerProxy", "NormalTaskListener"), - M3U8_PEER("com.arialyy.aria.core.download", "DownloadTask", "$$M3U8PeerListenerProxy", + M3U8_PEER("com.arialyy.aria.core.task", "DownloadTask", "$$M3U8PeerListenerProxy", "M3U8PeerTaskListener"); public String pkg, className, proxySuffix, proxySuperClass; diff --git a/AriaCompiler/src/main/java/com/arialyy/compiler/EntityInfo.java b/AriaCompiler/src/main/java/com/arialyy/compiler/EntityInfo.java index 27f35ced..20742573 100644 --- a/AriaCompiler/src/main/java/com/arialyy/compiler/EntityInfo.java +++ b/AriaCompiler/src/main/java/com/arialyy/compiler/EntityInfo.java @@ -20,7 +20,7 @@ package com.arialyy.compiler; * 实体信息 */ enum EntityInfo { - NORMAL("com.arialyy.aria.core.inf", "AbsNormalEntity"), + NORMAL("com.arialyy.aria.core.common", "AbsNormalEntity"), DOWNLOAD("com.arialyy.aria.core.download", "DownloadEntity"), UPLOAD("com.arialyy.aria.core.upload", "UploadEntity"); String pkg, className; diff --git a/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java b/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java deleted file mode 100644 index 9e18a5cd..00000000 --- a/AriaFtpComponent/src/androidTest/java/com/example/ariaftpcomponent/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.ariaftpcomponent; - -import android.content.Context; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - - assertEquals("com.example.ariaftpcomponent.test", appContext.getPackageName()); - } -} diff --git a/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java b/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java deleted file mode 100644 index 84ba97e3..00000000 --- a/AriaFtpComponent/src/main/java/com/arialyy/aria/ftpcomponent/bb.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.arialyy.aria.ftpcomponent; - -public class bb { -} diff --git a/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java b/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java deleted file mode 100644 index 79cfa215..00000000 --- a/AriaFtpComponent/src/test/java/com/example/ariaftpcomponent/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.ariaftpcomponent; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/AriaFtpComponent/.gitignore b/FtpComponent/.gitignore similarity index 100% rename from AriaFtpComponent/.gitignore rename to FtpComponent/.gitignore diff --git a/FtpComponent/build.gradle b/FtpComponent/build.gradle new file mode 100644 index 00000000..9417b467 --- /dev/null +++ b/FtpComponent/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" + + testImplementation 'junit:junit:4.12' + implementation project(path: ':AriaFtpPlug') + implementation project(path: ':PublicComponent') +} diff --git a/AriaFtpComponent/consumer-rules.pro b/FtpComponent/consumer-rules.pro similarity index 100% rename from AriaFtpComponent/consumer-rules.pro rename to FtpComponent/consumer-rules.pro diff --git a/AriaFtpComponent/proguard-rules.pro b/FtpComponent/proguard-rules.pro similarity index 100% rename from AriaFtpComponent/proguard-rules.pro rename to FtpComponent/proguard-rules.pro diff --git a/AriaFtpComponent/src/main/AndroidManifest.xml b/FtpComponent/src/main/AndroidManifest.xml similarity index 100% rename from AriaFtpComponent/src/main/AndroidManifest.xml rename to FtpComponent/src/main/AndroidManifest.xml diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpInfoThread.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java index a3345e2c..87f63342 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/AbsFtpInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.ftp; import android.net.TrafficStats; import android.os.Process; @@ -24,10 +24,11 @@ import aria.apache.commons.net.ftp.FTPClientConfig; import aria.apache.commons.net.ftp.FTPFile; import aria.apache.commons.net.ftp.FTPReply; import aria.apache.commons.net.ftp.FTPSClient; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; @@ -55,7 +56,7 @@ public abstract class AbsFtpInfoThread> - extends AbsThreadTask { - private final String TAG = "AbsFtpThreadTask"; +public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter { + + protected FtpTaskOption mTaskOption; protected String charSet; - protected AbsFtpThreadTask(SubThreadConfig config) { + protected BaseFtpThreadTaskAdapter(SubThreadConfig config) { super(config); + } protected void closeClient(FTPClient client) { @@ -61,7 +62,7 @@ public abstract class AbsFtpThreadTask { +public class FtpDirInfoThread extends AbsFtpInfoThread { - FtpDirInfoThread(DGTaskWrapper taskEntity, OnFileInfoCallback callback) { + public FtpDirInfoThread(DGTaskWrapper taskEntity, OnFileInfoCallback callback) { super(taskEntity, callback); } @Override protected String getRemotePath() { - return mTaskWrapper.asFtp().getUrlEntity().remotePath; + return mTaskOption.getUrlEntity().remotePath; } @Override protected void handleFile(String remotePath, FTPFile ftpFile) { @@ -59,7 +58,7 @@ class FtpDirInfoThread extends AbsFtpInfoThread(); + record.threadNum = threadNum; + + int requestType = getWrapper().getRequestType(); + if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) { + record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); + // 线程数为1,或者使用了分块,则认为是使用动态长度文件 + record.isOpenDynamicFile = threadNum == 1 || record.isBlock; + } else { + record.isBlock = false; + } + record.taskType = TaskRecord.TYPE_HTTP_FTP; + record.isGroupRecord = getEntity().isGroupChild(); + if (record.isGroupRecord) { + if (getEntity() instanceof DownloadEntity) { + record.dGroupHash = ((DownloadEntity) getEntity()).getGroupHash(); + } + } + + return record; + } + + @Override public int initTaskThreadNum() { + int requestType = getWrapper().getRequestType(); + if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) { + int threadNum = Configuration.getInstance().downloadCfg.getThreadNum(); + return getEntity().getFileSize() <= IRecordHandler.SUB_LEN + || getEntity().isGroupChild() + || threadNum == 1 + ? 1 + : threadNum; + } else { + return 1; + } + } + + +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpTaskConfig.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpTaskOption.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpTaskConfig.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpTaskOption.java index c4b5a332..e9a67d3e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpTaskConfig.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpTaskOption.java @@ -13,17 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.ftp; import aria.apache.commons.net.ftp.FTPClientConfig; -import com.arialyy.aria.core.inf.ITaskConfig; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; +import com.arialyy.aria.core.inf.ITaskOption; import java.lang.ref.SoftReference; import java.net.Proxy; /** * fTP任务设置的信息,如:用户名、密码、端口等信息 */ -public class FtpTaskConfig implements ITaskConfig { +public class FtpTaskOption implements ITaskOption { /** * 账号和密码 @@ -69,7 +72,7 @@ public class FtpTaskConfig implements ITaskConfig { } public IFtpUploadInterceptor getUploadInterceptor() { - return uploadInterceptor.get(); + return uploadInterceptor == null ? null : uploadInterceptor.get(); } public void setUploadInterceptor(IFtpUploadInterceptor uploadInterceptor) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/SSLSessionReuseFTPSClient.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/SSLSessionReuseFTPSClient.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/SSLSessionReuseFTPSClient.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/SSLSessionReuseFTPSClient.java index 63fed6e3..5e915e65 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/SSLSessionReuseFTPSClient.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/SSLSessionReuseFTPSClient.java @@ -1,4 +1,4 @@ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.ftp; import aria.apache.commons.net.ftp.FTPSClient; import java.io.IOException; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDFileInfoThread.java similarity index 83% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDFileInfoThread.java index a3f81b8c..a9f35116 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDFileInfoThread.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.ftp.download; import aria.apache.commons.net.ftp.FTPFile; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.common.ftp.AbsFtpInfoThread; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.exception.AriaIOException; +import com.arialyy.aria.ftp.AbsFtpInfoThread; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; @@ -29,10 +29,10 @@ import com.arialyy.aria.util.FileUtil; * Created by Aria.Lao on 2017/7/25. * 获取ftp文件信息 */ -class FtpFileInfoThread extends AbsFtpInfoThread { +class FtpDFileInfoThread extends AbsFtpInfoThread { private final String TAG = "FtpFileInfoThread"; - FtpFileInfoThread(DTaskWrapper taskEntity, OnFileInfoCallback callback) { + FtpDFileInfoThread(DTaskWrapper taskEntity, OnFileInfoCallback callback) { super(taskEntity, callback); } @@ -46,7 +46,7 @@ class FtpFileInfoThread extends AbsFtpInfoThread { } @Override protected String getRemotePath() { - return mTaskWrapper.asFtp().getUrlEntity().remotePath; + return mTaskOption.getUrlEntity().remotePath; } @Override protected void onPreComplete(int code) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java new file mode 100644 index 00000000..f15367f8 --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.download; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.ftp.FtpRecordAdapter; +import com.arialyy.aria.util.ALog; +import java.io.File; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +final class FtpDLoaderAdapter extends AbsNormalLoaderAdapter { + + FtpDLoaderAdapter(ITaskWrapper wrapper) { + super(wrapper); + } + + @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { + if (!record.isBlock) { + if (getTempFile().exists()) { + getTempFile().delete(); + } + //CommonUtil.createFile(mTempFile.getPath()); + } else { + for (int i = 0; i < totalThreadNum; i++) { + File blockFile = + new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); + if (blockFile.exists()) { + ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); + blockFile.delete(); + } + } + } + return true; + } + + @Override public IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask threadTask = new ThreadTask(config); + FtpDThreadTaskAdapter adapter = new FtpDThreadTaskAdapter(config); + threadTask.setAdapter(adapter); + return threadTask; + } + + @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { + FtpRecordAdapter adapter = new FtpRecordAdapter(wrapper); + RecordHandler handler = new RecordHandler(wrapper); + handler.setAdapter(adapter); + return handler; + } +} diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java new file mode 100644 index 00000000..64b8b474 --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.download; + +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IDLoadListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.ftp.FtpTaskOption; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +public class FtpDLoaderUtil extends AbsNormalLoaderUtil { + + public FtpDLoaderUtil(DTaskWrapper wrapper, IDLoadListener downloadListener) { + super(wrapper, downloadListener); + wrapper.generateTaskOption(FtpTaskOption.class); + } + + @Override protected AbsLoader createLoader() { + NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); + loader.setAdapter(new FtpDLoaderAdapter(getTaskWrapper())); + return loader; + } + + @Override protected Runnable createInfoThread() { + return new FtpDFileInfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String url, CompleteInfo info) { + ((NormalLoader) getLoader()).updateTempFile(); + getLoader().start(); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + fail(e, needRetry); + getLoader().closeTimer(); + } + }); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java similarity index 60% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java index a67e9e10..544ece5d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/FtpThreadTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.ftp.download; import aria.apache.commons.net.ftp.FTPClient; import aria.apache.commons.net.ftp.FTPReply; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.ftp.AbsFtpThreadTask; -import com.arialyy.aria.core.config.DownloadConfig; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.TaskException; +import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.CommonUtil; @@ -36,56 +33,54 @@ import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; /** - * Created by Aria.Lao on 2017/7/24. Ftp下载任务 + * @Author lyy + * @Date 2019-09-18 */ -class FtpThreadTask extends AbsFtpThreadTask { - private final String TAG = "FtpThreadTask"; +final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { - FtpThreadTask(SubThreadConfig config) { + FtpDThreadTaskAdapter(SubThreadConfig config) { super(config); } - @Override public FtpThreadTask call() throws Exception { - super.call(); - if (mRecord.isComplete) { + @Override protected void handlerThreadTask() { + if (getThreadRecord().isComplete) { handleComplete(); - return this; + return; } FTPClient client = null; InputStream is = null; try { ALog.d(TAG, - String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", getFileName(), - mRecord.threadId, mRecord.startLocation, mRecord.endLocation)); + String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", getTaskWrapper().getKey(), + getThreadRecord().threadId, getThreadRecord().startLocation, + getThreadRecord().endLocation)); client = createClient(); if (client == null) { - fail(mChildCurrentLocation, new TaskException(TAG, "ftp client 创建失败")); - return this; + fail(new TaskException(TAG, "ftp client 创建失败"), false); + return; } - if (mRecord.startLocation > 0) { - client.setRestartOffset(mRecord.startLocation); + if (getThreadRecord().startLocation > 0) { + client.setRestartOffset(getThreadRecord().startLocation); } //发送第二次指令时,还需要再做一次判断 int reply = client.getReplyCode(); if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.COMMAND_OK) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, - String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString()))); + fail(new AriaIOException(TAG, + String.format("获取文件信息错误,错误码为:%s,msg:%s", reply, client.getReplyString())), false); client.disconnect(); - return this; + return; } String remotePath = - CommonUtil.convertFtpChar(charSet, getTaskWrapper().asFtp().getUrlEntity().remotePath); + CommonUtil.convertFtpChar(charSet, mTaskOption.getUrlEntity().remotePath); ALog.i(TAG, String.format("remotePath【%s】", remotePath)); is = client.retrieveFileStream(remotePath); reply = client.getReplyCode(); if (!FTPReply.isPositivePreliminary(reply)) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, - String.format("获取流失败,错误码为:%s,msg:%s", reply, client.getReplyString()))); + fail(new AriaIOException(TAG, + String.format("获取流失败,错误码为:%s,msg:%s", reply, client.getReplyString())), true); client.disconnect(); - return this; + return; } if (getConfig().isOpenDynamicFile) { @@ -95,11 +90,9 @@ class FtpThreadTask extends AbsFtpThreadTask { handleComplete(); } } catch (IOException e) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e)); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); } catch (Exception e) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e)); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), false); } finally { try { if (is != null) { @@ -109,24 +102,20 @@ class FtpThreadTask extends AbsFtpThreadTask { e.printStackTrace(); } closeClient(client); - onThreadComplete(); } - return this; } /** * 处理线程完成的情况 */ private void handleComplete() { - if (isBreak()) { + if (getThreadTask().isBreak()) { return; } - if (!checkBlock()) { + if (!getThreadTask().checkBlock()) { return; } - ALog.i(TAG, String.format("任务【%s】线程__%s__下载完毕", getFileName(), mRecord.threadId)); - writeConfig(true, mRecord.endLocation); - sendCompleteMsg(); + complete(); } /** @@ -142,15 +131,15 @@ class FtpThreadTask extends AbsFtpThreadTask { foc = fos.getChannel(); fic = Channels.newChannel(is); ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); - while (isLive() && (len = fic.read(bf)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { mSpeedBandUtil.limitNextBytes(len); } - if (mChildCurrentLocation + len >= mRecord.endLocation) { - len = (int) (mRecord.endLocation - mChildCurrentLocation); + if (getRangeProgress() + len >= getThreadRecord().endLocation) { + len = (int) (getThreadRecord().endLocation - getRangeProgress()); bf.flip(); fos.write(bf.array(), 0, len); bf.compact(); @@ -165,8 +154,7 @@ class FtpThreadTask extends AbsFtpThreadTask { } handleComplete(); } catch (IOException e) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e)); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); } finally { try { if (fos != null) { @@ -192,18 +180,18 @@ class FtpThreadTask extends AbsFtpThreadTask { try { file = new BufferedRandomAccessFile(getConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); - file.seek(mRecord.startLocation); + file.seek(getThreadRecord().startLocation); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; - while (isLive() && (len = is.read(buffer)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { mSpeedBandUtil.limitNextBytes(len); } - if (mChildCurrentLocation + len >= mRecord.endLocation) { - len = (int) (mRecord.endLocation - mChildCurrentLocation); + if (getRangeProgress() + len >= getThreadRecord().endLocation) { + len = (int) (getThreadRecord().endLocation - getRangeProgress()); file.write(buffer, 0, len); progress(len); break; @@ -213,8 +201,7 @@ class FtpThreadTask extends AbsFtpThreadTask { } } } catch (IOException e) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e)); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); } finally { try { if (file != null) { @@ -225,12 +212,4 @@ class FtpThreadTask extends AbsFtpThreadTask { } } } - - @Override public int getMaxSpeed() { - return getTaskConfig().getMaxSpeed(); - } - - @Override protected DownloadConfig getTaskConfig() { - return getTaskWrapper().getConfig(); - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java similarity index 62% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java index 85935c0b..987ba33a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/FtpDirDownloadUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java @@ -13,18 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.ftp.download; import android.net.Uri; import android.text.TextUtils; -import com.arialyy.aria.core.common.ftp.FtpUrlEntity; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.ftp.FtpDirInfoThread; +import com.arialyy.aria.ftp.FtpTaskOption; +import com.arialyy.aria.core.group.AbsGroupUtil; +import com.arialyy.aria.core.group.AbsSubDLoadUtil; +import com.arialyy.aria.core.listener.IDGroupListener; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -32,26 +37,27 @@ import java.util.concurrent.locks.ReentrantLock; * Created by Aria.Lao on 2017/7/27. * ftp文件夹下载工具 */ -public class FtpDirDownloadUtil extends AbsGroupUtil { - private String TAG = "FtpDirDownloadUtil"; +public class FtpDirDLoaderUtil extends AbsGroupUtil { private ReentrantLock LOCK = new ReentrantLock(); private Condition condition = LOCK.newCondition(); - public FtpDirDownloadUtil(IDGroupListener listener, DGTaskWrapper taskEntity) { - super(listener, taskEntity); + public FtpDirDLoaderUtil(IDGroupListener listener, DGTaskWrapper wrapper) { + super(listener, wrapper); + wrapper.generateTaskOption(FtpTaskOption.class); } - @Override int getTaskType() { - return FTP_DIR; + @Override + protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) { + return new SubDLoaderUtil(getScheduler(), wrapper, needGetFileInfo); } - @Override protected boolean onPreStart() { - super.onPreStart(); + @Override protected boolean onStart() { + super.onStart(); - if (mGTWrapper.getEntity().getFileSize() > 1) { + if (getWrapper().getEntity().getFileSize() > 1) { startDownload(true); } else { - FtpDirInfoThread infoThread = new FtpDirInfoThread(mGTWrapper, new OnFileInfoCallback() { + FtpDirInfoThread infoThread = new FtpDirInfoThread(getWrapper(), new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { if (info.code >= 200 && info.code < 300) { startDownload(false); @@ -86,24 +92,26 @@ public class FtpDirDownloadUtil extends AbsGroupUtil { LOCK.unlock(); } initState(); - for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { + for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) { if (needCloneInfo) { cloneInfo(wrapper); } if (wrapper.getState() != IEntity.STATE_COMPLETE) { - createAndStartSubLoader(wrapper); + startSubLoader(createSubLoader(wrapper, true)); } } } private void cloneInfo(DTaskWrapper subWrapper) { - FtpUrlEntity urlEntity = mGTWrapper.asFtp().getUrlEntity().clone(); + FtpTaskOption option = (FtpTaskOption) getWrapper().getTaskOption(); + FtpUrlEntity urlEntity = option.getUrlEntity().clone(); Uri uri = Uri.parse(subWrapper.getEntity().getUrl()); String remotePath = uri.getPath(); urlEntity.remotePath = TextUtils.isEmpty(remotePath) ? "/" : remotePath; - subWrapper.asFtp().setUrlEntity(urlEntity); - subWrapper.asFtp().setCharSet(mGTWrapper.asFtp().getCharSet()); - subWrapper.asFtp().setProxy(mGTWrapper.asFtp().getProxy()); + FtpTaskOption subOption = ((FtpTaskOption) subWrapper.getTaskOption()); + subOption.setUrlEntity(urlEntity); + subOption.setCharSet(option.getCharSet()); + subOption.setProxy(option.getProxy()); } } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/SubDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/SubDLoaderUtil.java new file mode 100644 index 00000000..4512e1d6 --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/SubDLoaderUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.download; + +import android.os.Handler; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.ftp.FtpTaskOption; +import com.arialyy.aria.core.group.AbsSubDLoadUtil; +import com.arialyy.aria.core.group.ChildDLoadListener; + +/** + * @Author lyy + * @Date 2019-09-28 + */ +class SubDLoaderUtil extends AbsSubDLoadUtil { + /** + * @param schedulers 调度器 + * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 + */ + SubDLoaderUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) { + super(schedulers, taskWrapper, needGetInfo); + taskWrapper.generateTaskOption(FtpTaskOption.class); + } + + @Override protected NormalLoader createLoader(ChildDLoadListener listener, DTaskWrapper wrapper) { + NormalLoader loader = new NormalLoader(listener, wrapper); + FtpDLoaderAdapter adapter = new FtpDLoaderAdapter(wrapper); + loader.setAdapter(adapter); + return loader; + } + + @Override public void start() { + getDownloader().start(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFISAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFISAdapter.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java index c85a0308..ae78eeea 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFISAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.uploader; +package com.arialyy.aria.ftp.upload; import androidx.annotation.NonNull; import com.arialyy.aria.util.BufferedRandomAccessFile; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java index 3be4fbb1..e13a174e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.uploader; +package com.arialyy.aria.ftp.upload; import android.text.TextUtils; import aria.apache.commons.net.ftp.FTPClient; import aria.apache.commons.net.ftp.FTPFile; -import com.arialyy.aria.core.common.ftp.AbsFtpInfoThread; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; -import com.arialyy.aria.core.common.ftp.FtpInterceptHandler; -import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.ftp.AbsFtpInfoThread; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.DbDataHelper; @@ -38,7 +38,7 @@ import java.util.List; * Created by Aria.Lao on 2017/9/26. * 单任务上传远程服务器文件信息 */ -class FtpFileInfoThread extends AbsFtpInfoThread { +class FtpUFileInfoThread extends AbsFtpInfoThread { private static final String TAG = "FtpUploadFileInfoThread"; static final int CODE_COMPLETE = 0xab1; private boolean isComplete = false; @@ -48,13 +48,13 @@ class FtpFileInfoThread extends AbsFtpInfoThread { */ private boolean useInterceptor = false; - FtpFileInfoThread(UTaskWrapper taskEntity, OnFileInfoCallback callback) { + FtpUFileInfoThread(UTaskWrapper taskEntity, OnFileInfoCallback callback) { super(taskEntity, callback); } @Override protected String getRemotePath() { return remotePath == null ? - mTaskWrapper.asFtp().getUrlEntity().remotePath + "/" + mEntity.getFileName() : remotePath; + mTaskOption.getUrlEntity().remotePath + "/" + mEntity.getFileName() : remotePath; } @Override protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) { @@ -63,7 +63,7 @@ class FtpFileInfoThread extends AbsFtpInfoThread { return true; } try { - IFtpUploadInterceptor interceptor = mTaskWrapper.asFtp().getUploadInterceptor(); + IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor(); if (interceptor != null) { useInterceptor = true; List files = new ArrayList<>(); @@ -89,10 +89,10 @@ class FtpFileInfoThread extends AbsFtpInfoThread { } else if (!TextUtils.isEmpty(interceptHandler.getNewFileName())) { ALog.i(TAG, String.format("远端已拥有同名文件,将修改remotePath,原文件名:%s,新文件名:%s", mEntity.getFileName(), interceptHandler.getNewFileName())); - remotePath = mTaskWrapper.asFtp().getUrlEntity().remotePath + remotePath = mTaskOption.getUrlEntity().remotePath + "/" + interceptHandler.getNewFileName(); - mTaskWrapper.asFtp().setNewFileName(interceptHandler.getNewFileName()); + mTaskOption.setNewFileName(interceptHandler.getNewFileName()); closeClient(client); run(); return false; diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java new file mode 100644 index 00000000..1589bc78 --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.upload; + +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IUploadListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.ftp.FtpTaskOption; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +public class FtpULoaderUtil extends AbsNormalLoaderUtil { + + public FtpULoaderUtil(DTaskWrapper wrapper, IUploadListener uploadListener) { + super(wrapper, uploadListener); + wrapper.generateTaskOption(FtpTaskOption.class); + } + + @Override protected AbsLoader createLoader() { + NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); + loader.setAdapter(new FtpULoaferAdapter(getTaskWrapper())); + return null; + } + + @Override protected Runnable createInfoThread() { + return new FtpUFileInfoThread((UTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String url, CompleteInfo info) { + if (info.code == FtpUFileInfoThread.CODE_COMPLETE) { + getListener().onComplete(); + } else { + getLoader().start(); + } + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + fail(e, needRetry); + } + }); + } +} diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java new file mode 100644 index 00000000..f3a44b2b --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.upload; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.ftp.FtpRecordAdapter; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +class FtpULoaferAdapter extends AbsNormalLoaderAdapter { + + FtpULoaferAdapter(ITaskWrapper wrapper) { + super(wrapper); + } + + @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { + return true; + } + + @Override public IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask threadTask = new ThreadTask(config); + FtpUThreadTaskAdapter adapter = new FtpUThreadTaskAdapter(config); + threadTask.setAdapter(adapter); + return threadTask; + } + + @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { + FtpRecordAdapter adapter = new FtpRecordAdapter(wrapper); + RecordHandler handler = new RecordHandler(wrapper); + handler.setAdapter(adapter); + return handler; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java similarity index 63% rename from Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index a41e3f9a..c988d83b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/FtpThreadTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -13,19 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.uploader; +package com.arialyy.aria.ftp.upload; import android.text.TextUtils; import aria.apache.commons.net.ftp.FTPClient; import aria.apache.commons.net.ftp.FTPReply; import aria.apache.commons.net.ftp.OnFtpInputStreamListener; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.ftp.AbsFtpThreadTask; -import com.arialyy.aria.core.common.ftp.FtpTaskConfig; -import com.arialyy.aria.core.config.UploadConfig; -import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.exception.AriaIOException; +import com.arialyy.aria.ftp.BaseFtpThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.CommonUtil; @@ -35,67 +32,58 @@ import java.io.UnsupportedEncodingException; /** * Created by Aria.Lao on 2017/7/28. D_FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限 */ -class FtpThreadTask extends AbsFtpThreadTask { +class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private final String TAG = "FtpThreadTask"; private String dir, remotePath; - FtpThreadTask(SubThreadConfig config) { + FtpUThreadTaskAdapter(SubThreadConfig config) { super(config); } - @Override public int getMaxSpeed() { - return getTaskConfig().getMaxSpeed(); - } - - @Override protected UploadConfig getTaskConfig() { - return getTaskWrapper().getConfig(); - } - - @Override public FtpThreadTask call() throws Exception { - super.call(); + @Override protected void handlerThreadTask() { FTPClient client = null; BufferedRandomAccessFile file = null; try { ALog.d(TAG, - String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", getFileName(), - mRecord.threadId, mRecord.startLocation, mRecord.endLocation)); + String.format("任务【%s】线程__%s__开始上传【开始位置 : %s,结束位置:%s】", getEntity().getKey(), + getThreadRecord().threadId, getThreadRecord().startLocation, + getThreadRecord().endLocation)); client = createClient(); if (client == null) { - return this; + return; } initPath(); client.makeDirectory(dir); client.changeWorkingDirectory(dir); - client.setRestartOffset(mRecord.startLocation); + client.setRestartOffset(getThreadRecord().startLocation); int reply = client.getReplyCode(); if (!FTPReply.isPositivePreliminary(reply) && reply != FTPReply.FILE_ACTION_OK) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, - String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, - client.getReplyString(), getEntity().getFilePath()))); + fail(new AriaIOException(TAG, + String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, + client.getReplyString(), getEntity().getFilePath())), false); client.disconnect(); - return this; + return; } file = new BufferedRandomAccessFile(getConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); - if (mRecord.startLocation != 0) { + if (getThreadRecord().startLocation != 0) { //file.skipBytes((int) getConfig().START_LOCATION); - file.seek(mRecord.startLocation); + file.seek(getThreadRecord().startLocation); } boolean complete = upload(client, file); - if (!complete || isBreak()) { - return this; + if (!complete || getThreadTask().isBreak()) { + return; } - ALog.i(TAG, String.format("任务【%s】线程__%s__上传完毕", getFileName(), mRecord.threadId)); - writeConfig(true, mRecord.endLocation); - sendCompleteMsg(); + ALog.i(TAG, + String.format("任务【%s】线程__%s__上传完毕", getEntity().getKey(), getThreadRecord().threadId)); + complete(); } catch (IOException e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, + fail(new AriaIOException(TAG, String.format("上传失败,filePath: %s, uploadUrl: %s", getEntity().getFilePath(), - getConfig().url))); + getConfig().url)), true); } catch (Exception e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, null, e)); + fail(new AriaIOException(TAG, null, e), false); } finally { try { if (file != null) { @@ -105,23 +93,24 @@ class FtpThreadTask extends AbsFtpThreadTask { e.printStackTrace(); } closeClient(client); - onThreadComplete(); } - return this; + } + + private UploadEntity getEntity() { + return (UploadEntity) getTaskWrapper().getEntity(); } private void initPath() throws UnsupportedEncodingException { - FtpTaskConfig delegate = getTaskWrapper().asFtp(); - dir = CommonUtil.convertFtpChar(charSet, delegate.getUrlEntity().remotePath); + dir = CommonUtil.convertFtpChar(charSet, mTaskOption.getUrlEntity().remotePath); String fileName = - TextUtils.isEmpty(delegate.getNewFileName()) ? CommonUtil.convertFtpChar(charSet, + TextUtils.isEmpty(mTaskOption.getNewFileName()) ? CommonUtil.convertFtpChar(charSet, getEntity().getFileName()) - : CommonUtil.convertFtpChar(charSet, delegate.getNewFileName()); + : CommonUtil.convertFtpChar(charSet, mTaskOption.getNewFileName()); remotePath = CommonUtil.convertFtpChar(charSet, - String.format("%s/%s", delegate.getUrlEntity().remotePath, fileName)); + String.format("%s/%s", mTaskOption.getUrlEntity().remotePath, fileName)); } /** @@ -140,7 +129,7 @@ class FtpThreadTask extends AbsFtpThreadTask { @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, int bytesTransferred, long streamSize) { try { - if (isBreak() && !isStoped) { + if (getThreadTask().isBreak() && !isStoped) { isStoped = true; client.abor(); } @@ -162,8 +151,7 @@ class FtpThreadTask extends AbsFtpThreadTask { if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); } else { - fail(mChildCurrentLocation, - new AriaIOException(TAG, msg, e)); + fail(new AriaIOException(TAG, msg, e), true); } return false; } @@ -171,10 +159,9 @@ class FtpThreadTask extends AbsFtpThreadTask { int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { if (reply != FTPReply.TRANSFER_ABORTED) { - fail(mChildCurrentLocation, - new AriaIOException(TAG, - String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, - client.getReplyString(), getEntity().getFilePath())), false); + fail(new AriaIOException(TAG, + String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, client.getReplyString(), + getEntity().getFilePath())), false); } if (client.isConnected()) { client.disconnect(); diff --git a/AriaFtpComponent/src/main/res/values/strings.xml b/FtpComponent/src/main/res/values/strings.xml similarity index 100% rename from AriaFtpComponent/src/main/res/values/strings.xml rename to FtpComponent/src/main/res/values/strings.xml diff --git a/HttpComponent/.gitignore b/HttpComponent/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/HttpComponent/.gitignore @@ -0,0 +1 @@ +/build diff --git a/HttpComponent/build.gradle b/HttpComponent/build.gradle new file mode 100644 index 00000000..290fc2ad --- /dev/null +++ b/HttpComponent/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" + + testImplementation 'junit:junit:4.12' + implementation project(path: ':PublicComponent') +} diff --git a/HttpComponent/consumer-rules.pro b/HttpComponent/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/HttpComponent/proguard-rules.pro b/HttpComponent/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/HttpComponent/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/HttpComponent/src/main/AndroidManifest.xml b/HttpComponent/src/main/AndroidManifest.xml new file mode 100644 index 00000000..bb0d2bdf --- /dev/null +++ b/HttpComponent/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/BaseHttpThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/BaseHttpThreadTaskAdapter.java new file mode 100644 index 00000000..07e81f36 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/BaseHttpThreadTaskAdapter.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http; + +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.task.AbsThreadTaskAdapter; + +/** + * @Author lyy + * @Date 2019-09-22 + */ +public abstract class BaseHttpThreadTaskAdapter extends AbsThreadTaskAdapter { + protected HttpTaskOption mTaskOption; + + protected BaseHttpThreadTaskAdapter(SubThreadConfig config) { + super(config); + mTaskOption = (HttpTaskOption) getTaskWrapper().getTaskOption(); + } + + protected String getFileName() { + return getEntity().getFileName(); + } + + protected AbsNormalEntity getEntity() { + return (AbsNormalEntity) getTaskWrapper().getEntity(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java b/HttpComponent/src/main/java/com/arialyy/aria/http/ChunkedInputStream.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/ChunkedInputStream.java index 04ce9390..360cf06f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ChunkedInputStream.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/ChunkedInputStream.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.http; import com.arialyy.aria.util.ALog; import java.io.DataInputStream; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java b/HttpComponent/src/main/java/com/arialyy/aria/http/ConnectionHelp.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/ConnectionHelp.java index 9124c5a6..3c92381f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/ConnectionHelp.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/ConnectionHelp.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.http; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.ProtocolType; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.ProtocolType; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.common.http.HttpTaskConfig; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.SSLContextUtil; @@ -52,7 +51,7 @@ public class ConnectionHelp { * * @throws MalformedURLException */ - public static URL handleUrl(String url, HttpTaskConfig taskDelegate) + public static URL handleUrl(String url, HttpTaskOption taskDelegate) throws MalformedURLException { Map params = taskDelegate.getParams(); if (params != null && taskDelegate.getRequestEnum() == RequestEnum.GET) { @@ -99,7 +98,7 @@ public class ConnectionHelp { * * @throws IOException */ - public static HttpURLConnection handleConnection(URL url, HttpTaskConfig taskDelegate) + public static HttpURLConnection handleConnection(URL url, HttpTaskOption taskDelegate) throws IOException { HttpURLConnection conn; URLConnection urlConn; @@ -109,11 +108,11 @@ public class ConnectionHelp { urlConn = url.openConnection(); } if (urlConn instanceof HttpsURLConnection) { - AriaManager manager = AriaManager.getInstance(); + AriaConfig config = AriaConfig.getInstance(); conn = (HttpsURLConnection) urlConn; SSLContext sslContext = - SSLContextUtil.getSSLContextFromAssets(manager.getDownloadConfig().getCaName(), - manager.getDownloadConfig().getCaPath(), ProtocolType.Default); + SSLContextUtil.getSSLContextFromAssets(config.getDConfig().getCaName(), + config.getDConfig().getCaPath(), ProtocolType.Default); if (sslContext == null) { sslContext = SSLContextUtil.getDefaultSLLContext(ProtocolType.Default); } @@ -131,7 +130,7 @@ public class ConnectionHelp { * * @throws ProtocolException */ - public static HttpURLConnection setConnectParam(HttpTaskConfig delegate, HttpURLConnection conn) { + public static HttpURLConnection setConnectParam(HttpTaskOption delegate, HttpURLConnection conn) { if (delegate.getRequestEnum() == RequestEnum.POST) { conn.setDoInput(true); conn.setDoOutput(true); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java index 857a7268..121f5e07 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java @@ -13,20 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.http; import android.net.TrafficStats; import android.net.Uri; import android.os.Process; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.common.http.HttpTaskConfig; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.TaskException; @@ -61,15 +60,14 @@ public class HttpFileInfoThread implements Runnable { private DTaskWrapper mTaskWrapper; private int mConnectTimeOut; private OnFileInfoCallback onFileInfoCallback; - private HttpTaskConfig mTaskDelegate; + private HttpTaskOption taskOption; public HttpFileInfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { this.mTaskWrapper = taskWrapper; mEntity = taskWrapper.getEntity(); - mConnectTimeOut = - AriaManager.getInstance().getDownloadConfig().getConnectTimeOut(); + mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut(); onFileInfoCallback = callback; - mTaskDelegate = taskWrapper.asHttp(); + taskOption = (HttpTaskOption) taskWrapper.getTaskOption(); } @Override public void run() { @@ -77,9 +75,9 @@ public class HttpFileInfoThread implements Runnable { TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode()); HttpURLConnection conn = null; try { - URL url = ConnectionHelp.handleUrl(mEntity.getUrl(), mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(mEntity.getUrl(), taskOption); + conn = ConnectionHelp.handleConnection(url, taskOption); + ConnectionHelp.setConnectParam(taskOption, conn); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setConnectTimeout(mConnectTimeOut); conn.connect(); @@ -97,8 +95,8 @@ public class HttpFileInfoThread implements Runnable { } private void handleConnect(HttpURLConnection conn) throws IOException { - if (mTaskDelegate.getRequestEnum() == RequestEnum.POST) { - Map params = mTaskDelegate.getParams(); + if (taskOption.getRequestEnum() == RequestEnum.POST) { + Map params = taskOption.getParams(); if (params != null) { OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream()); Set keys = params.keySet(); @@ -114,7 +112,7 @@ public class HttpFileInfoThread implements Runnable { } } - IHttpFileLenAdapter lenAdapter = mTaskWrapper.asHttp().getFileLenAdapter(); + IHttpFileLenAdapter lenAdapter = taskOption.getFileLenAdapter(); if (lenAdapter == null) { lenAdapter = new FileLenAdapter(); } else { @@ -144,7 +142,7 @@ public class HttpFileInfoThread implements Runnable { Map> headers = conn.getHeaderFields(); String disposition = conn.getHeaderField("Content-Disposition"); - if (mTaskDelegate.isUseServerFileName()) { + if (taskOption.isUseServerFileName()) { if (!TextUtils.isEmpty(disposition)) { mEntity.setDisposition(CommonUtil.encryptBASE64(disposition)); handleContentDisposition(disposition); @@ -159,7 +157,7 @@ public class HttpFileInfoThread implements Runnable { for (String cookie : cookiesHeader) { msCookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0)); } - mTaskDelegate.setCookieManager(msCookieManager); + taskOption.setCookieManager(msCookieManager); } mTaskWrapper.setCode(code); @@ -219,7 +217,7 @@ public class HttpFileInfoThread implements Runnable { conn.getResponseMessage(), mEntity.getUrl())), true); } if (end) { - mTaskDelegate.setChunked(isChunked); + taskOption.setChunked(isChunked); if (onFileInfoCallback != null) { CompleteInfo info = new CompleteInfo(code, mTaskWrapper); onFileInfoCallback.onComplete(mEntity.getUrl(), info); @@ -302,14 +300,14 @@ public class HttpFileInfoThread implements Runnable { failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false); return; } - mTaskDelegate.setRedirectUrl(newUrl); + taskOption.setRedirectUrl(newUrl); mEntity.setRedirect(true); mEntity.setRedirectUrl(newUrl); String cookies = conn.getHeaderField("Set-Cookie"); conn.disconnect(); - URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(newUrl, taskOption); + conn = ConnectionHelp.handleConnection(url, taskOption); + ConnectionHelp.setConnectParam(taskOption, conn); conn.setRequestProperty("Cookie", cookies); conn.setRequestProperty("Range", "bytes=" + 0 + "-"); conn.setConnectTimeout(mConnectTimeOut); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java new file mode 100644 index 00000000..1955c1be --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; +import com.arialyy.aria.core.common.RecordHelper; +import com.arialyy.aria.core.config.Configuration; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.RecordUtil; +import java.util.ArrayList; + +/** + * @Author lyy + * @Date 2019-09-23 + */ +public class HttpRecordAdapter extends AbsRecordHandlerAdapter { + public HttpRecordAdapter(AbsTaskWrapper wrapper) { + super(wrapper); + } + + @Override public void handlerTaskRecord(TaskRecord record) { + RecordHelper helper = new RecordHelper(getWrapper(), record); + if (record.isBlock) { + helper.handleBlockRecord(); + } else if (!getWrapper().isSupportBP()) { + helper.handleNoSupportBPRecord(); + } else { + helper.handleSingleThreadRecord(); + } + } + + @Override + public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { + ThreadRecord tr; + tr = new ThreadRecord(); + tr.taskKey = record.filePath; + tr.threadId = threadId; + tr.startLocation = startL; + tr.isComplete = false; + + tr.threadType = TaskRecord.TYPE_HTTP_FTP; + //最后一个线程的结束位置即为文件的总长度 + if (threadId == (record.threadNum - 1)) { + endL = getEntity().getFileSize(); + } + tr.endLocation = endL; + tr.blockLen = RecordUtil.getBlockLen(getEntity().getFileSize(), threadId, record.threadNum); + return tr; + } + + @Override public TaskRecord createTaskRecord(int threadNum) { + TaskRecord record = new TaskRecord(); + record.fileName = getEntity().getFileName(); + record.filePath = getEntity().getFilePath(); + record.threadRecords = new ArrayList<>(); + record.threadNum = threadNum; + + int requestType = getWrapper().getRequestType(); + if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) { + record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); + // 线程数为1,或者使用了分块,则认为是使用动态长度文件 + record.isOpenDynamicFile = threadNum == 1 || record.isBlock; + } else { + record.isBlock = false; + } + record.taskType = TaskRecord.TYPE_HTTP_FTP; + record.isGroupRecord = getEntity().isGroupChild(); + if (record.isGroupRecord) { + if (getEntity() instanceof DownloadEntity) { + record.dGroupHash = ((DownloadEntity) getEntity()).getGroupHash(); + } + } + + return record; + } + + @Override public int initTaskThreadNum() { + int requestTpe = getWrapper().getRequestType(); + if (requestTpe == ITaskWrapper.U_HTTP + || (requestTpe == ITaskWrapper.D_HTTP && (!getWrapper().isSupportBP()) + || ((HttpTaskOption) getWrapper().getTaskOption()).isChunked())) { + return 1; + } + int threadNum = Configuration.getInstance().downloadCfg.getThreadNum(); + return getEntity().getFileSize() <= IRecordHandler.SUB_LEN + || getEntity().isGroupChild() + || threadNum == 1 + ? 1 + : threadNum; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/http/HttpTaskConfig.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/common/http/HttpTaskConfig.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java index 096b29d0..d013f42e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/http/HttpTaskConfig.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.arialyy.aria.core.common.http; +package com.arialyy.aria.http; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; -import com.arialyy.aria.core.inf.ITaskConfig; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.inf.ITaskOption; import java.lang.ref.SoftReference; import java.net.CookieManager; import java.net.Proxy; @@ -28,7 +28,7 @@ import java.util.Map; /** * Http任务设置的信息,如:cookie、请求参数 */ -public class HttpTaskConfig implements ITaskConfig { +public class HttpTaskOption implements ITaskOption { private CookieManager cookieManager; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/DGroupUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java similarity index 67% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/DGroupUtil.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index 272583ef..65229f10 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/DGroupUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.http.download; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.common.OnFileInfoCallback; -import com.arialyy.aria.core.common.http.HttpTaskConfig; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.downloader.HttpFileInfoThread; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.core.group.AbsGroupUtil; +import com.arialyy.aria.core.group.AbsSubDLoadUtil; +import com.arialyy.aria.core.listener.IDGroupListener; +import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; @@ -38,19 +40,20 @@ import java.util.concurrent.Executors; * Created by AriaL on 2017/6/30. * 任务组下载工具 */ -public class DGroupUtil extends AbsGroupUtil implements IUtil { - private static final String TAG = "DownloadGroupUtil"; +public class DGroupLoaderUtil extends AbsGroupUtil { private final Object LOCK = new Object(); private ExecutorService mPool = null; private boolean getLenComplete = false; private List mTempWrapper = new ArrayList<>(); - public DGroupUtil(IDGroupListener listener, DGTaskWrapper taskWrapper) { + public DGroupLoaderUtil(IDGroupListener listener, DGTaskWrapper taskWrapper) { super(listener, taskWrapper); + taskWrapper.generateTaskOption(HttpTaskOption.class); } - @Override int getTaskType() { - return HTTP_GROUP; + @Override + protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) { + return new SubDLoaderUtil(getScheduler(), wrapper, needGetFileInfo); } @Override public void onPreCancel() { @@ -68,14 +71,14 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { return false; } - @Override protected boolean onPreStart() { - super.onPreStart(); + @Override protected boolean onStart() { + super.onStart(); initState(); - if (mState.getCompleteNum() == mState.getSubSize()) { + if (getState().getCompleteNum() == getState().getSubSize()) { mListener.onComplete(); } else { // 处理组合任务大小未知的情况 - if (mGTWrapper.isUnknownSize() && mGTWrapper.getEntity().getFileSize() < 1) { + if (getWrapper().isUnknownSize() && getWrapper().getEntity().getFileSize() < 1) { mPool = Executors.newCachedThreadPool(); getGroupSize(); try { @@ -87,9 +90,9 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { } return getLenComplete; } else { - for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { + for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) { if (wrapper.getState() != IEntity.STATE_COMPLETE) { - createAndStartSubLoader(wrapper); + startSubLoader(createSubLoader(wrapper, true)); } } } @@ -106,12 +109,12 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { int failCount; @Override public void run() { - for (DTaskWrapper dTaskWrapper : mGTWrapper.getSubTaskWrapper()) { + for (DTaskWrapper dTaskWrapper : getWrapper().getSubTaskWrapper()) { cloneHeader(dTaskWrapper); mPool.submit(new HttpFileInfoThread(dTaskWrapper, new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { - if (!mGTWrapper.isUnknownSize()) { - createAndStartSubLoader((DTaskWrapper) info.wrapper, false); + if (!getWrapper().isUnknownSize()) { + startSubLoader(createSubLoader((DTaskWrapper) info.wrapper, false)); } else { mTempWrapper.add((DTaskWrapper) info.wrapper); } @@ -127,7 +130,7 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { mListener.onSubFail((DownloadEntity) entity, new AriaIOException(TAG, String.format("子任务获取文件长度失败,url:%s", ((DownloadEntity) entity).getUrl()))); checkGetSizeComplete(count, failCount); - mState.countFailNum(entity.getKey()); + getState().countFailNum(entity.getKey()); } })); } @@ -139,26 +142,26 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { * 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小 */ private void checkGetSizeComplete(int count, int failCount) { - if (failCount == mGTWrapper.getSubTaskWrapper().size()) { - mState.isRunning = false; + if (failCount == getWrapper().getSubTaskWrapper().size()) { + getState().setRunning(false); mListener.onFail(false, new AriaIOException(TAG, "获取子任务长度失败")); notifyLock(); return; } - if (count == mGTWrapper.getSubTaskWrapper().size()) { + if (count == getWrapper().getSubTaskWrapper().size()) { long size = 0; - for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { + for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) { size += wrapper.getEntity().getFileSize(); } - mGTWrapper.getEntity().setConvertFileSize(CommonUtil.formatFileSize(size)); - mGTWrapper.getEntity().setFileSize(size); - mGTWrapper.getEntity().update(); + getWrapper().getEntity().setConvertFileSize(CommonUtil.formatFileSize(size)); + getWrapper().getEntity().setFileSize(size); + getWrapper().getEntity().update(); getLenComplete = true; ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s,失败的只任务数:%s", size, failCount)); // 未知大小的组合任务,延迟下载 - if (mGTWrapper.isUnknownSize()) { + if (getWrapper().isUnknownSize()) { for (DTaskWrapper wrapper : mTempWrapper) { - createAndStartSubLoader(wrapper, false); + startSubLoader(createSubLoader(wrapper, false)); } } notifyLock(); @@ -175,14 +178,14 @@ public class DGroupUtil extends AbsGroupUtil implements IUtil { * 子任务使用父包裹器的属性 */ private void cloneHeader(DTaskWrapper taskWrapper) { - HttpTaskConfig groupDelegate = mGTWrapper.asHttp(); - HttpTaskConfig subDelegate = taskWrapper.asHttp(); + HttpTaskOption groupOption = (HttpTaskOption) getWrapper().getTaskOption(); + HttpTaskOption subOption = (HttpTaskOption) taskWrapper.getTaskOption(); // 设置属性 - subDelegate.setFileLenAdapter(groupDelegate.getFileLenAdapter()); - subDelegate.setRequestEnum(groupDelegate.getRequestEnum()); - subDelegate.setHeaders(groupDelegate.getHeaders()); - subDelegate.setProxy(groupDelegate.getProxy()); - subDelegate.setParams(groupDelegate.getParams()); + subOption.setFileLenAdapter(groupOption.getFileLenAdapter()); + subOption.setRequestEnum(groupOption.getRequestEnum()); + subOption.setHeaders(groupOption.getHeaders()); + subOption.setProxy(groupOption.getProxy()); + subOption.setParams(groupOption.getParams()); } } \ No newline at end of file diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java new file mode 100644 index 00000000..442b9816 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http.download; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.http.HttpRecordAdapter; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.BufferedRandomAccessFile; +import java.io.File; +import java.io.IOException; + +/** + * @Author lyy + * @Date 2019-09-21 + */ +final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter { + HttpDLoaderAdapter(ITaskWrapper wrapper) { + super(wrapper); + } + + @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { + if (!record.isBlock) { + if (getTempFile().exists()) { + getTempFile().delete(); + } + //CommonUtil.createFile(mTempFile.getPath()); + } else { + for (int i = 0; i < totalThreadNum; i++) { + File blockFile = + new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); + if (blockFile.exists()) { + ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); + blockFile.delete(); + } + } + } + BufferedRandomAccessFile file = null; + try { + if (totalThreadNum > 1 && !record.isBlock) { + file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); + //设置文件长度 + file.setLength(getEntity().getFileSize()); + } + return true; + } catch (IOException e) { + e.printStackTrace(); + ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), + getEntity().getUrl())); + } finally { + if (file != null) { + try { + file.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return false; + } + + @Override public IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask task = new ThreadTask(config); + HttpDThreadTaskAdapter adapter = new HttpDThreadTaskAdapter(config); + task.setAdapter(adapter); + return task; + } + + @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { + RecordHandler recordHandler = new RecordHandler(wrapper); + HttpRecordAdapter adapter = new HttpRecordAdapter(wrapper); + recordHandler.setAdapter(adapter); + return recordHandler; + } + + private DownloadEntity getEntity() { + return (DownloadEntity) getWrapper().getEntity(); + } +} diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java new file mode 100644 index 00000000..d66025e1 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http.download; + +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpTaskOption; + +/** + * @Author lyy + * @Date 2019-09-21 + */ +public class HttpDLoaderUtil extends AbsNormalLoaderUtil { + public HttpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { + super(wrapper, listener); + wrapper.generateTaskOption(HttpTaskOption.class); + } + + @Override protected AbsLoader createLoader() { + NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); + HttpDLoaderAdapter adapter = new HttpDLoaderAdapter(getTaskWrapper()); + loader.setAdapter(adapter); + return loader; + } + + @Override protected Runnable createInfoThread() { + return new HttpFileInfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String url, CompleteInfo info) { + ((NormalLoader) getLoader()).updateTempFile(); + getLoader().start(); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + fail(e, needRetry); + getLoader().closeTimer(); + } + }); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java similarity index 70% rename from Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index d396ba46..69b63cb2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpThreadTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.downloader; +package com.arialyy.aria.http.download; -import com.arialyy.aria.core.common.AbsThreadTask; -import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.config.DownloadConfig; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.common.http.HttpTaskConfig; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.TaskException; +import com.arialyy.aria.http.BaseHttpThreadTaskAdapter; +import com.arialyy.aria.http.ConnectionHelp; +import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import java.io.BufferedInputStream; @@ -45,46 +43,48 @@ import java.util.Set; /** * Created by lyy on 2017/1/18. 下载线程 */ -final class HttpThreadTask extends AbsThreadTask { +final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { private final String TAG = "HttpThreadTask"; + private DTaskWrapper mTaskWrapper; - HttpThreadTask(SubThreadConfig config) { + HttpDThreadTaskAdapter(SubThreadConfig config) { super(config); } - @Override public HttpThreadTask call() throws Exception { - super.call(); - if (mRecord.isComplete) { + @Override protected void handlerThreadTask() { + mTaskWrapper = (DTaskWrapper) getTaskWrapper(); + if (getThreadRecord().isComplete) { handleComplete(); - return this; + return; } HttpURLConnection conn = null; BufferedInputStream is = null; BufferedRandomAccessFile file = null; try { - HttpTaskConfig taskDelegate = getTaskWrapper().asHttp(); - URL url = ConnectionHelp.handleUrl(getConfig().url, taskDelegate); - conn = ConnectionHelp.handleConnection(url, taskDelegate); + URL url = ConnectionHelp.handleUrl(getConfig().url, mTaskOption); + conn = ConnectionHelp.handleConnection(url, mTaskOption); if (mTaskWrapper.isSupportBP()) { ALog.d(TAG, String.format("任务【%s】线程__%s__开始下载【开始位置 : %s,结束位置:%s】", getFileName(), - mRecord.threadId, mRecord.startLocation, mRecord.endLocation)); - conn.setRequestProperty("Range", String.format("bytes=%s-%s", mRecord.startLocation, - (mRecord.endLocation - 1))); + getThreadRecord().threadId, getThreadRecord().startLocation, + getThreadRecord().endLocation)); + conn.setRequestProperty("Range", + String.format("bytes=%s-%s", getThreadRecord().startLocation, + (getThreadRecord().endLocation - 1))); } else { ALog.w(TAG, "该下载不支持断点"); } - ConnectionHelp.setConnectParam(taskDelegate, conn); + ConnectionHelp.setConnectParam(mTaskOption, conn); conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 - if (taskDelegate.isChunked()) { + if (mTaskOption.isChunked()) { conn.setDoInput(true); conn.setChunkedStreamingMode(0); } conn.connect(); // 传递参数 - if (taskDelegate.getRequestEnum() == RequestEnum.POST) { - Map params = taskDelegate.getParams(); + if (mTaskOption.getRequestEnum() == RequestEnum.POST) { + Map params = mTaskOption.getParams(); if (params != null) { OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream()); Set keys = params.keySet(); @@ -101,7 +101,7 @@ final class HttpThreadTask extends AbsThreadTask { } is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); - if (taskDelegate.isChunked()) { + if (mTaskOption.isChunked()) { readChunked(is); } else if (getConfig().isOpenDynamicFile) { readDynamicFile(is); @@ -111,22 +111,22 @@ final class HttpThreadTask extends AbsThreadTask { new BufferedRandomAccessFile(getConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); //设置每条线程写入文件的位置 - file.seek(mRecord.startLocation); + file.seek(getThreadRecord().startLocation); readNormal(is, file); handleComplete(); } } catch (MalformedURLException e) { - fail(mChildCurrentLocation, new TaskException(TAG, + fail(new TaskException(TAG, String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getDownloadPath(), getEntity().getUrl()), e)); + getEntity().getFilePath(), getEntity().getUrl()), e), false); } catch (IOException e) { - fail(mChildCurrentLocation, new TaskException(TAG, + fail(new TaskException(TAG, String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getDownloadPath(), getEntity().getUrl()), e)); + getEntity().getFilePath(), getEntity().getUrl()), e), true); } catch (Exception e) { - fail(mChildCurrentLocation, new TaskException(TAG, + fail(new TaskException(TAG, String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getDownloadPath(), getEntity().getUrl()), e)); + getEntity().getFilePath(), getEntity().getUrl()), e), false); } finally { try { if (file != null) { @@ -141,10 +141,7 @@ final class HttpThreadTask extends AbsThreadTask { } catch (IOException e) { e.printStackTrace(); } - - onThreadComplete(); } - return this; } /** @@ -156,8 +153,8 @@ final class HttpThreadTask extends AbsThreadTask { fos = new FileOutputStream(getConfig().tempFile, true); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; - while (isLive() && (len = is.read(buffer)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { @@ -168,10 +165,9 @@ final class HttpThreadTask extends AbsThreadTask { } handleComplete(); } catch (IOException e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, - String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(), - getConfig().url), - e)); + fail(new AriaIOException(TAG, + String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), + getConfig().url)), true); } finally { if (fos != null) { try { @@ -198,15 +194,15 @@ final class HttpThreadTask extends AbsThreadTask { ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); //如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。 - while (isLive() && (len = fic.read(bf)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { mSpeedBandUtil.limitNextBytes(len); } - if (mChildCurrentLocation + len >= mRecord.endLocation) { - len = (int) (mRecord.endLocation - mChildCurrentLocation); + if (getRangeProgress() + len >= getThreadRecord().endLocation) { + len = (int) (getThreadRecord().endLocation - getRangeProgress()); bf.flip(); fos.write(bf.array(), 0, len); bf.compact(); @@ -221,10 +217,9 @@ final class HttpThreadTask extends AbsThreadTask { } handleComplete(); } catch (IOException e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, - String.format("文件下载失败,savePath: %s, url: %s", getEntity().getDownloadPath(), - getConfig().url), - e)); + fail(new AriaIOException(TAG, + String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), + getConfig().url), e), true); } finally { try { if (fos != null) { @@ -250,8 +245,8 @@ final class HttpThreadTask extends AbsThreadTask { throws IOException { byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; - while (isLive() && (len = is.read(buffer)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { @@ -266,31 +261,13 @@ final class HttpThreadTask extends AbsThreadTask { * 处理完成配置文件的更新或事件回调 */ private void handleComplete() { - if (isBreak()) { - return; - } - if (!checkBlock()) { + if (getThreadTask().isBreak()) { return; } - if (getTaskWrapper().asHttp().isChunked()) { - sendCompleteMsg(); + if (!getThreadTask().checkBlock()) { return; } - //支持断点的处理 - if (mTaskWrapper.isSupportBP()) { - writeConfig(true, mRecord.endLocation); - sendCompleteMsg(); - } else { - sendCompleteMsg(); - } - } - - @Override public int getMaxSpeed() { - return mAridManager.getDownloadConfig().getMaxSpeed(); - } - - @Override protected DownloadConfig getTaskConfig() { - return getTaskWrapper().getConfig(); + complete(); } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/SubDLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/SubDLoaderUtil.java new file mode 100644 index 00000000..a32b0699 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/SubDLoaderUtil.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http.download; + +import android.os.Handler; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.core.group.AbsSubDLoadUtil; +import com.arialyy.aria.core.group.ChildDLoadListener; +import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpTaskOption; + +/** + * @Author lyy + * @Date 2019-09-28 + */ +class SubDLoaderUtil extends AbsSubDLoadUtil { + /** + * @param schedulers 调度器 + * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 + */ + SubDLoaderUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) { + super(schedulers, taskWrapper, needGetInfo); + taskWrapper.generateTaskOption(HttpTaskOption.class); + } + + @Override protected NormalLoader createLoader(ChildDLoadListener listener, DTaskWrapper wrapper) { + NormalLoader loader = new NormalLoader(listener, wrapper); + HttpDLoaderAdapter adapter = new HttpDLoaderAdapter(wrapper); + loader.setAdapter(adapter); + return loader; + } + + @Override public void start() { + if (isNeedGetInfo()) { + new Thread(new HttpFileInfoThread(getWrapper(), new OnFileInfoCallback() { + + @Override public void onComplete(String url, CompleteInfo info) { + getDownloader().start(); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + getSchedulers().obtainMessage(ISchedulers.FAIL, SubDLoaderUtil.this).sendToTarget(); + } + })).start(); + } else { + getDownloader().start(); + } + } +} + diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java new file mode 100644 index 00000000..e8899840 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http.upload; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.http.HttpRecordAdapter; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +final class HttpULoaderAdapter extends AbsNormalLoaderAdapter { + public HttpULoaderAdapter(ITaskWrapper wrapper) { + super(wrapper); + } + + @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { + return true; + } + + @Override public IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask task = new ThreadTask(config); + HttpUThreadTaskAdapter adapter = new HttpUThreadTaskAdapter(config); + task.setAdapter(adapter); + return task; + } + + @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { + RecordHandler recordHandler = new RecordHandler(wrapper); + HttpRecordAdapter adapter = new HttpRecordAdapter(wrapper); + recordHandler.setAdapter(adapter); + return recordHandler; + } +} diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java new file mode 100644 index 00000000..be15e5ac --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http.upload; + +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.http.HttpTaskOption; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +public class HttpULoaderUtil extends AbsNormalLoaderUtil { + protected HttpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { + super(wrapper, listener); + wrapper.generateTaskOption(HttpTaskOption.class); + } + + @Override protected AbsLoader createLoader() { + NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); + HttpULoaderAdapter adapter = new HttpULoaderAdapter(getTaskWrapper()); + loader.setAdapter(adapter); + return loader; + } + + @Override protected Runnable createInfoThread() { + return new Runnable() { + @Override public void run() { + getLoader().start(); + } + }; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java similarity index 75% rename from Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java index 009c3283..67e3b21d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/uploader/HttpThreadTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload.uploader; +package com.arialyy.aria.http.upload; import android.text.TextUtils; -import com.arialyy.aria.core.common.AbsThreadTask; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.http.HttpTaskConfig; -import com.arialyy.aria.core.config.UploadConfig; -import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.TaskException; +import com.arialyy.aria.http.BaseHttpThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.io.BufferedReader; @@ -44,34 +41,31 @@ import java.util.UUID; /** * Created by Aria.Lao on 2017/7/28. 不支持断点的HTTP上传任务 */ -class HttpThreadTask extends AbsThreadTask { - private final String TAG = "HttpThreadTask"; +final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter { private final String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成 private final String PREFIX = "--", LINE_END = "\r\n"; private HttpURLConnection mHttpConn; private OutputStream mOutputStream; - HttpThreadTask(SubThreadConfig config) { + HttpUThreadTaskAdapter(SubThreadConfig config) { super(config); } - @Override public HttpThreadTask call() throws Exception { - super.call(); + @Override protected void handlerThreadTask() { File uploadFile = new File(getEntity().getFilePath()); if (!uploadFile.exists()) { fail(new TaskException(TAG, String.format("上传失败,文件不存在;filePath: %s, url: %s", getEntity().getFilePath(), getEntity().getUrl()))); - return this; + return; } URL url; try { - HttpTaskConfig taskDelegate = getTaskWrapper().asHttp(); url = new URL(CommonUtil.convertUrl(getConfig().url)); mHttpConn = (HttpURLConnection) url.openConnection(); - mHttpConn.setRequestMethod(taskDelegate.getRequestEnum().name); + mHttpConn.setRequestMethod(mTaskOption.getRequestEnum().name); mHttpConn.setUseCaches(false); mHttpConn.setDoOutput(true); mHttpConn.setDoInput(true); @@ -86,15 +80,15 @@ class HttpThreadTask extends AbsThreadTask { mHttpConn.setChunkedStreamingMode(getTaskConfig().getBuffSize()); //添加Http请求头部 - Set keys = taskDelegate.getHeaders().keySet(); + Set keys = mTaskOption.getHeaders().keySet(); for (String key : keys) { - mHttpConn.setRequestProperty(key, taskDelegate.getHeaders().get(key)); + mHttpConn.setRequestProperty(key, mTaskOption.getHeaders().get(key)); } mOutputStream = mHttpConn.getOutputStream(); PrintWriter writer = - new PrintWriter(new OutputStreamWriter(mOutputStream, taskDelegate.getCharSet()), true); + new PrintWriter(new OutputStreamWriter(mOutputStream, mTaskOption.getCharSet()), true); // 添加参数 - Map params = taskDelegate.getParams(); + Map params = mTaskOption.getParams(); if (params != null && !params.isEmpty()) { for (String key : params.keySet()) { addFormField(writer, key, params.get(key)); @@ -102,28 +96,24 @@ class HttpThreadTask extends AbsThreadTask { } //添加文件上传表单字段 - keys = taskDelegate.getFormFields().keySet(); + keys = mTaskOption.getFormFields().keySet(); for (String key : keys) { - addFormField(writer, key, taskDelegate.getFormFields().get(key)); + addFormField(writer, key, mTaskOption.getFormFields().get(key)); } - uploadFile(writer, taskDelegate.getAttachment(), uploadFile); - getEntity().setResponseStr(finish(writer)); - sendCompleteMsg(); + uploadFile(writer, mTaskOption.getAttachment(), uploadFile); + complete(); } catch (Exception e) { e.printStackTrace(); fail(new TaskException(TAG, String.format("上传失败,filePath: %s, url: %s", getEntity().getFilePath(), getEntity().getUrl()), e)); - } finally { - onThreadComplete(); } - return this; } private void fail(BaseException e1) { try { - sendFailMsg(e1); + fail(e1, false); if (mOutputStream != null) { mOutputStream.close(); } @@ -133,20 +123,16 @@ class HttpThreadTask extends AbsThreadTask { } private String getContentType() { - HttpTaskConfig config = getTaskWrapper().asHttp(); - return - (config.getHeaders() == null || TextUtils.isEmpty(config.getHeaders().get("Content-Type"))) - ? - "multipart/form-data" : config.getHeaders().get("Content-Type"); + return (mTaskOption.getHeaders() == null || TextUtils.isEmpty( + mTaskOption.getHeaders().get("Content-Type"))) ? "multipart/form-data" + : mTaskOption.getHeaders().get("Content-Type"); } private String getUserAgent() { - HttpTaskConfig config = getTaskWrapper().asHttp(); - return - (config.getHeaders() == null || TextUtils.isEmpty(config.getHeaders().get("User-Agent"))) - ? - "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)" - : config.getHeaders().get("User-Agent"); + return (mTaskOption.getHeaders() == null || TextUtils.isEmpty( + mTaskOption.getHeaders().get("User-Agent"))) + ? "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)" + : mTaskOption.getHeaders().get("User-Agent"); } /** @@ -159,7 +145,7 @@ class HttpThreadTask extends AbsThreadTask { .append("\"") .append(LINE_END); writer.append("Content-Type: text/plain; charset=") - .append(getTaskWrapper().asHttp().getCharSet()) + .append(mTaskOption.getCharSet()) .append(LINE_END); writer.append(LINE_END); writer.append(value).append(LINE_END); @@ -194,7 +180,7 @@ class HttpThreadTask extends AbsThreadTask { while ((bytesRead = inputStream.read(buffer)) != -1) { progress(bytesRead); mOutputStream.write(buffer, 0, bytesRead); - if (isCancel) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { @@ -231,7 +217,7 @@ class HttpThreadTask extends AbsThreadTask { if (status == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpConn.getInputStream())); String line; - while (isLive() && (line = reader.readLine()) != null) { + while (getThreadTask().isLive() && (line = reader.readLine()) != null) { response.append(line); } reader.close(); @@ -246,11 +232,7 @@ class HttpThreadTask extends AbsThreadTask { return response.toString(); } - @Override public int getMaxSpeed() { - return getTaskConfig().getMaxSpeed(); - } - - @Override protected UploadConfig getTaskConfig() { - return getTaskWrapper().getConfig(); + @Override protected UploadEntity getEntity() { + return (UploadEntity) super.getEntity(); } } diff --git a/HttpComponent/src/main/res/values/strings.xml b/HttpComponent/src/main/res/values/strings.xml new file mode 100644 index 00000000..c76105c3 --- /dev/null +++ b/HttpComponent/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + HttpComponent + diff --git a/M3U8Component/.gitignore b/M3U8Component/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/M3U8Component/.gitignore @@ -0,0 +1 @@ +/build diff --git a/M3U8Component/build.gradle b/M3U8Component/build.gradle new file mode 100644 index 00000000..6df0cb47 --- /dev/null +++ b/M3U8Component/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" + implementation project(path: ':HttpComponent') + implementation project(path: ':PublicComponent') +} diff --git a/M3U8Component/consumer-rules.pro b/M3U8Component/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/M3U8Component/proguard-rules.pro b/M3U8Component/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/M3U8Component/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/M3U8Component/src/main/AndroidManifest.xml b/M3U8Component/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d1b50eee --- /dev/null +++ b/M3U8Component/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java similarity index 77% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java index e4312db9..c6b17fbe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/BaseM3U8Loader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8; -import com.arialyy.aria.core.common.AbsFileer; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; import java.io.BufferedReader; @@ -30,10 +33,12 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; -public abstract class BaseM3U8Loader extends AbsFileer { +public abstract class BaseM3U8Loader extends AbsLoader { + protected M3U8TaskOption mM3U8Option; - BaseM3U8Loader(IEventListener listener, DTaskWrapper wrapper) { + public BaseM3U8Loader(IEventListener listener, DTaskWrapper wrapper) { super(listener, wrapper); + mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); mTempFile = new File(wrapper.getEntity().getFilePath()); } @@ -51,8 +56,8 @@ public abstract class BaseM3U8Loader extends AbsFileer mInfos = new ArrayList<>(); - interface OnGetLivePeerCallback { + public interface OnGetLivePeerCallback { void onGetPeer(String url); } - M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { + public M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { this.mTaskWrapper = taskWrapper; mEntity = taskWrapper.getEntity(); - mConnectTimeOut = - AriaManager.getInstance().getDownloadConfig().getConnectTimeOut(); + mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut(); onFileInfoCallback = callback; - mTaskDelegate = taskWrapper.asHttp(); + mHttpOption = (HttpTaskOption) taskWrapper.getTaskOption(); + mM3U8Option = (M3U8TaskOption) taskWrapper.getM3u8Option(); mEntity.getM3U8Entity().setLive(mTaskWrapper.getRequestType() == AbsTaskWrapper.M3U8_LIVE); } @@ -93,9 +96,9 @@ final class M3U8InfoThread implements Runnable { TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode()); HttpURLConnection conn = null; try { - URL url = ConnectionHelp.handleUrl(mEntity.getUrl(), mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(mEntity.getUrl(), mHttpOption); + conn = ConnectionHelp.handleConnection(url, mHttpOption); + ConnectionHelp.setConnectParam(mHttpOption, conn); conn.setConnectTimeout(mConnectTimeOut); conn.connect(); handleConnect(conn); @@ -145,7 +148,7 @@ final class M3U8InfoThread implements Runnable { extInf.add(info); } } else if (line.startsWith("#EXT-X-STREAM-INF")) { - int setBand = mTaskWrapper.asM3U8().getBandWidth(); + int setBand = mM3U8Option.getBandWidth(); int bandWidth = getBandWidth(line); // 多码率的m3u8配置文件,清空信息 if (isGenerateIndexFile && mInfos != null) { @@ -252,8 +255,9 @@ final class M3U8InfoThread implements Runnable { m3U8Entity.method = param.split("=")[1]; } else if (param.startsWith("URI")) { m3U8Entity.keyUrl = param.split("=")[1].replaceAll("\"", ""); - m3U8Entity.keyPath = new File(mEntity.getFilePath()).getParent() + "/" + CommonUtil.getStrMd5( - m3U8Entity.keyUrl) + ".key"; + m3U8Entity.keyPath = + new File(mEntity.getFilePath()).getParent() + "/" + CommonUtil.getStrMd5( + m3U8Entity.keyUrl) + ".key"; } else if (param.startsWith("IV")) { m3U8Entity.iv = param.split("=")[1]; } @@ -294,14 +298,14 @@ final class M3U8InfoThread implements Runnable { failDownload("下载失败,重定向url错误", false); return; } - mTaskDelegate.setRedirectUrl(newUrl); + mHttpOption.setRedirectUrl(newUrl); mEntity.setRedirect(true); mEntity.setRedirectUrl(newUrl); String cookies = conn.getHeaderField("Set-Cookie"); conn.disconnect(); // 关闭上一个连接 - URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(newUrl, mHttpOption); + conn = ConnectionHelp.handleConnection(url, mHttpOption); + ConnectionHelp.setConnectParam(mHttpOption, conn); conn.setRequestProperty("Cookie", cookies); conn.setConnectTimeout(mConnectTimeOut); conn.connect(); @@ -313,7 +317,7 @@ final class M3U8InfoThread implements Runnable { * 处理码率 */ private void handleBandWidth(HttpURLConnection conn, String bandWidthM3u8Url) throws IOException { - IBandWidthUrlConverter converter = mTaskWrapper.asM3U8().getBandWidthUrlConverter(); + IBandWidthUrlConverter converter = mM3U8Option.getBandWidthUrlConverter(); if (converter != null) { bandWidthM3u8Url = converter.convert(bandWidthM3u8Url); if (!bandWidthM3u8Url.startsWith("http")) { @@ -323,13 +327,13 @@ final class M3U8InfoThread implements Runnable { } else { ALog.d(TAG, "没有设置码率转换器"); } - mTaskWrapper.asM3U8().setBandWidthUrl(bandWidthM3u8Url); + mM3U8Option.setBandWidthUrl(bandWidthM3u8Url); ALog.d(TAG, String.format("新码率url:%s", bandWidthM3u8Url)); String cookies = conn.getHeaderField("Set-Cookie"); conn.disconnect(); // 关闭上一个连接 - URL url = ConnectionHelp.handleUrl(bandWidthM3u8Url, mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(bandWidthM3u8Url, mHttpOption); + conn = ConnectionHelp.handleConnection(url, mHttpOption); + ConnectionHelp.setConnectParam(mHttpOption, conn); conn.setRequestProperty("Cookie", cookies); conn.setConnectTimeout(mConnectTimeOut); conn.connect(); @@ -355,9 +359,9 @@ final class M3U8InfoThread implements Runnable { } else { return; } - URL url = ConnectionHelp.handleUrl(info.keyUrl, mTaskDelegate); - conn = ConnectionHelp.handleConnection(url, mTaskDelegate); - ConnectionHelp.setConnectParam(mTaskDelegate, conn); + URL url = ConnectionHelp.handleUrl(info.keyUrl, mHttpOption); + conn = ConnectionHelp.handleConnection(url, mHttpOption); + ConnectionHelp.setConnectParam(mHttpOption, conn); conn.setConnectTimeout(mConnectTimeOut); conn.connect(); InputStream is = conn.getInputStream(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/M3U8Listener.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java similarity index 82% rename from Aria/src/main/java/com/arialyy/aria/core/download/M3U8Listener.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java index 6c46eae1..c2b97fcb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/M3U8Listener.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java @@ -13,22 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.m3u8; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import com.arialyy.aria.core.inf.IDownloadListener; +import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.listener.BaseDListener; +import com.arialyy.aria.core.listener.IDLoadListener; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; /** * 下载监听类 */ -public class M3U8Listener extends BaseDListener implements IDownloadListener { +public class M3U8Listener extends BaseDListener implements IDLoadListener { - M3U8Listener(DownloadTask task, Handler outHandler) { + M3U8Listener(AbsTask task, Handler outHandler) { super(task, outHandler); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java new file mode 100644 index 00000000..06193d55 --- /dev/null +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.m3u8; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.ALog; +import java.io.File; +import java.util.ArrayList; + +/** + * @Author lyy + * @Date 2019-09-24 + */ +public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { + private M3U8TaskOption mOption; + + public M3U8RecordAdapter(DTaskWrapper wrapper) { + super(wrapper); + mOption = (M3U8TaskOption) wrapper.getM3u8Option(); + } + + /** + * 不处理live的记录 + */ + @Override public void handlerTaskRecord(TaskRecord mTaskRecord) { + String cacheDir = mOption.getCacheDir(); + long currentProgress = 0; + int completeNum = 0; + + M3U8Entity m3U8Entity = ((DownloadEntity) getEntity()).getM3U8Entity(); + // 重新下载所有切片 + boolean reDownload = + (m3U8Entity.getPeerNum() <= 0 || (m3U8Entity.isGenerateIndexFile() && !new File( + String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath())).exists())); + + for (ThreadRecord record : mTaskRecord.threadRecords) { + File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); + if (!record.isComplete || reDownload) { + if (temp.exists()) { + temp.delete(); + } + record.startLocation = 0; + //ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId)); + } else { + if (!temp.exists()) { + record.startLocation = 0; + record.isComplete = false; + ALog.w(TAG, String.format("分片【%s】不存在,将重新下载该分片", record.threadId)); + } else { + completeNum++; + currentProgress += temp.length(); + } + } + } + mOption.setCompleteNum(completeNum); + getEntity().setCurrentProgress(currentProgress); + mTaskRecord.bandWidth = mOption.getBandWidth(); + } + + /** + * 不处理live的记录 + * + * @param record 任务记录 + * @param threadId 线程id + * @param startL 线程开始位置 + * @param endL 线程结束位置 + */ + @Override + public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { + ThreadRecord tr; + tr = new ThreadRecord(); + tr.taskKey = record.filePath; + tr.threadId = threadId; + tr.isComplete = false; + tr.startLocation = 0; + tr.threadType = TaskRecord.TYPE_M3U8_VOD; + tr.tsUrl = mOption.getUrls().get(threadId); + return tr; + } + + @Override public TaskRecord createTaskRecord(int threadNum) { + TaskRecord record = new TaskRecord(); + record.fileName = getEntity().getFileName(); + record.filePath = getEntity().getFilePath(); + record.threadRecords = new ArrayList<>(); + record.threadNum = threadNum; + + int requestType = getWrapper().getRequestType(); + if (requestType == ITaskWrapper.M3U8_VOD) { + record.taskType = TaskRecord.TYPE_M3U8_VOD; + record.isOpenDynamicFile = true; + record.bandWidth = mOption.getBandWidth(); + } else if (requestType == ITaskWrapper.M3U8_LIVE) { + record.taskType = TaskRecord.TYPE_M3U8_LIVE; + record.isOpenDynamicFile = true; + record.bandWidth = mOption.getBandWidth(); + } + return record; + } + + @Override public int initTaskThreadNum() { + if (getWrapper().getRequestType() == ITaskWrapper.M3U8_VOD) { + return mOption.getUrls().size(); + } + if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { + return 1; + } + return 0; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index c1854005..bad1cbfc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8TaskConfig.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -13,15 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.inf.ITaskOption; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; import java.lang.ref.SoftReference; import java.util.List; /** * m3u8任务配信息 */ -public class M3U8TaskConfig { +public class M3U8TaskOption implements ITaskOption { /** * 所有ts文件的下载地址 @@ -46,7 +51,7 @@ public class M3U8TaskConfig { /** * 合并处理器 */ - private ITsMergeHandler mergeHandler; + private SoftReference mergeHandler; /** * 已完成的ts分片数量 @@ -131,7 +136,7 @@ public class M3U8TaskConfig { } public ILiveTsUrlConverter getLiveTsUrlConverter() { - return liveTsUrlConverter.get(); + return liveTsUrlConverter == null ? null : liveTsUrlConverter.get(); } public void setLiveTsUrlConverter(ILiveTsUrlConverter liveTsUrlConverter) { @@ -187,15 +192,15 @@ public class M3U8TaskConfig { } public ITsMergeHandler getMergeHandler() { - return mergeHandler; + return mergeHandler == null ? null : mergeHandler.get(); } public void setMergeHandler(ITsMergeHandler mergeHandler) { - this.mergeHandler = mergeHandler; + this.mergeHandler = new SoftReference<>(mergeHandler); } public IVodTsUrlConverter getVodUrlConverter() { - return vodUrlConverter.get(); + return vodUrlConverter == null ? null : vodUrlConverter.get(); } public void setVodUrlConverter(IVodTsUrlConverter vodUrlConverter) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8ThreadTask.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java similarity index 70% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8ThreadTask.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java index 6b9365d3..b4c2e4c6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8ThreadTask.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8; -import com.arialyy.aria.core.common.AbsThreadTask; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.http.HttpTaskConfig; -import com.arialyy.aria.core.config.DownloadConfig; -import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.downloader.ConnectionHelp; +import com.arialyy.aria.core.task.AbsThreadTaskAdapter; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.TaskException; +import com.arialyy.aria.http.ConnectionHelp; +import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.util.ALog; import java.io.BufferedInputStream; import java.io.FileOutputStream; @@ -45,37 +43,37 @@ import java.util.Set; /** * Created by lyy on 2017/1/18. 下载线程 */ -final class M3U8ThreadTask extends AbsThreadTask { +public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { private final String TAG = "M3U8ThreadTask"; + private HttpTaskOption mHttpTaskOption; - M3U8ThreadTask(SubThreadConfig config) { + public M3U8ThreadTaskAdapter(SubThreadConfig config) { super(config); + mHttpTaskOption = (HttpTaskOption) getTaskWrapper().getTaskOption(); } - @Override public M3U8ThreadTask call() throws Exception { - super.call(); - if (mRecord.isComplete) { + @Override protected void handlerThreadTask() { + if (getThreadRecord().isComplete) { handleComplete(); - return this; + return; } HttpURLConnection conn = null; BufferedInputStream is = null; try { - HttpTaskConfig taskDelegate = getTaskWrapper().asHttp(); - URL url = ConnectionHelp.handleUrl(getConfig().url, taskDelegate); - conn = ConnectionHelp.handleConnection(url, taskDelegate); - ALog.d(TAG, String.format("分片【%s】开始下载", mRecord.threadId)); - ConnectionHelp.setConnectParam(taskDelegate, conn); + URL url = ConnectionHelp.handleUrl(getConfig().url, mHttpTaskOption); + conn = ConnectionHelp.handleConnection(url, mHttpTaskOption); + ALog.d(TAG, String.format("分片【%s】开始下载", getThreadRecord().threadId)); + ConnectionHelp.setConnectParam(mHttpTaskOption, conn); conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 - if (taskDelegate.isChunked()) { + if (mHttpTaskOption.isChunked()) { conn.setDoInput(true); conn.setChunkedStreamingMode(0); } conn.connect(); // 传递参数 - if (taskDelegate.getRequestEnum() == RequestEnum.POST) { - Map params = taskDelegate.getParams(); + if (mHttpTaskOption.getRequestEnum() == RequestEnum.POST) { + Map params = mHttpTaskOption.getParams(); if (params != null) { OutputStreamWriter dos = new OutputStreamWriter(conn.getOutputStream()); Set keys = params.keySet(); @@ -92,23 +90,23 @@ final class M3U8ThreadTask extends AbsThreadTask { } is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); - if (taskDelegate.isChunked()) { + if (mHttpTaskOption.isChunked()) { readChunked(is); } else if (getConfig().isOpenDynamicFile) { readDynamicFile(is); } } catch (MalformedURLException e) { - fail(mChildCurrentLocation, new TaskException(TAG, - String.format("分片【%s】下载失败,filePath: %s, url: %s", mRecord.threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e)); + fail(new TaskException(TAG, + String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, + getConfig().tempFile.getPath(), getEntity().getUrl()), e), false); } catch (IOException e) { - fail(mChildCurrentLocation, new TaskException(TAG, - String.format("分片【%s】下载失败,filePath: %s, url: %s", mRecord.threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e)); + fail(new TaskException(TAG, + String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, + getConfig().tempFile.getPath(), getEntity().getUrl()), e), true); } catch (Exception e) { - fail(mChildCurrentLocation, new TaskException(TAG, - String.format("分片【%s】下载失败,filePath: %s, url: %s", mRecord.threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e)); + fail(new TaskException(TAG, + String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, + getConfig().tempFile.getPath(), getEntity().getUrl()), e), false); } finally { try { if (is != null) { @@ -120,9 +118,7 @@ final class M3U8ThreadTask extends AbsThreadTask { } catch (IOException e) { e.printStackTrace(); } - onThreadComplete(); } - return this; } /** @@ -134,8 +130,8 @@ final class M3U8ThreadTask extends AbsThreadTask { fos = new FileOutputStream(getConfig().tempFile, true); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; - while (isLive() && (len = is.read(buffer)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { @@ -146,10 +142,9 @@ final class M3U8ThreadTask extends AbsThreadTask { } handleComplete(); } catch (IOException e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, + fail(new AriaIOException(TAG, String.format("文件下载失败,savePath: %s, url: %s", getConfig().tempFile.getPath(), - getConfig().url), - e)); + getConfig().url), e), true); } finally { if (fos != null) { try { @@ -176,8 +171,8 @@ final class M3U8ThreadTask extends AbsThreadTask { ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); //如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。 - while (isLive() && (len = fic.read(bf)) != -1) { - if (isBreak()) { + while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) { + if (getThreadTask().isBreak()) { break; } if (mSpeedBandUtil != null) { @@ -190,10 +185,9 @@ final class M3U8ThreadTask extends AbsThreadTask { } handleComplete(); } catch (IOException e) { - fail(mChildCurrentLocation, new AriaIOException(TAG, + fail(new AriaIOException(TAG, String.format("文件下载失败,savePath: %s, url: %s", getConfig().tempFile.getPath(), - getConfig().url), - e)); + getConfig().url), e), true); } finally { try { if (fos != null) { @@ -212,32 +206,17 @@ final class M3U8ThreadTask extends AbsThreadTask { } } + private DownloadEntity getEntity() { + return (DownloadEntity) getTaskWrapper().getEntity(); + } + /** * 处理完成配置文件的更新或事件回调 */ private void handleComplete() { - if (isBreak()) { + if (getThreadTask().isBreak()) { return; } - if (getTaskWrapper().asHttp().isChunked()) { - sendCompleteMsg(); - return; - } - - //支持断点的处理 - if (mTaskWrapper.isSupportBP()) { - writeConfig(true, getConfig().tempFile.length()); - sendCompleteMsg(); - } else { - sendCompleteMsg(); - } - } - - @Override public int getMaxSpeed() { - return mAridManager.getDownloadConfig().getMaxSpeed(); - } - - @Override protected DownloadConfig getTaskConfig() { - return getTaskWrapper().getConfig(); + complete(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java similarity index 83% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveLoader.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 7986096c..b7433ca3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -13,21 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8.live; import android.os.Handler; import android.os.Looper; import android.os.Message; -import com.arialyy.aria.core.common.IThreadState; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.manager.ThreadTaskManager; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.m3u8.BaseM3U8Loader; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.m3u8.IdGenerator; +import com.arialyy.aria.m3u8.M3U8Listener; +import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; -import com.arialyy.aria.util.IdGenerator; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; @@ -52,11 +57,11 @@ public class M3U8LiveLoader extends BaseM3U8Loader { private Condition mCondition = LOCK.newCondition(); private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); - M3U8LiveLoader(IEventListener listener, DTaskWrapper wrapper) { + public M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) { super(listener, wrapper); } - @Override protected IThreadState getStateManager(Looper looper) { + @Override protected IThreadState createStateManager(Looper looper) { mManager = new LiveStateManager(looper, mListener); mStateHandler = new Handler(looper, mManager); return mManager; @@ -80,7 +85,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { if (url == null) { break; } - M3U8ThreadTask task = createThreadTask(cacheDir, index, url); + ThreadTask task = createThreadTask(cacheDir, index, url); getTaskList().put(index, task); mFlagQueue.offer(startThreadTask(task)); index++; @@ -98,6 +103,10 @@ public class M3U8LiveLoader extends BaseM3U8Loader { }).start(); } + @Override public long getFileSize() { + return mTempFile.length(); + } + private void notifyLock() { try { LOCK.lock(); @@ -116,7 +125,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { * * @return 线程唯一id标志 */ - private long startThreadTask(M3U8ThreadTask task) { + private long startThreadTask(ThreadTask task) { ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task); return IdGenerator.getInstance().nextId(); } @@ -124,7 +133,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { /** * 配置config */ - private M3U8ThreadTask createThreadTask(String cacheDir, int indexId, String tsUrl) { + private ThreadTask createThreadTask(String cacheDir, int indexId, String tsUrl) { ThreadRecord record = new ThreadRecord(); record.taskKey = mRecord.filePath; record.isComplete = false; @@ -132,7 +141,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { record.threadType = TaskRecord.TYPE_M3U8_LIVE; record.threadId = indexId; - SubThreadConfig config = new SubThreadConfig<>(); + SubThreadConfig config = new SubThreadConfig(); config.url = tsUrl; config.tempFile = new File(getTsFilePath(cacheDir, indexId)); config.isBlock = mRecord.isBlock; @@ -144,7 +153,10 @@ public class M3U8LiveLoader extends BaseM3U8Loader { if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile.getPath()); } - return new M3U8ThreadTask(config); + ThreadTask threadTask = new ThreadTask(config); + M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config); + threadTask.setAdapter(adapter); + return threadTask; } /** @@ -156,7 +168,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { if (getEntity().getM3U8Entity().isGenerateIndexFile()) { return generateIndexFile(); } - ITsMergeHandler mergeHandler = mTaskWrapper.asM3U8().getMergeHandler(); + ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); String cacheDir = getCacheDir(); List partPath = new ArrayList<>(); String[] tsNames = new File(cacheDir).list(new FilenameFilter() { @@ -172,7 +184,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { if (mergeHandler != null) { isSuccess = mergeHandler.merge(getEntity().getM3U8Entity(), partPath); } else { - isSuccess = FileUtil.mergeFile(mEntity.getFilePath(), partPath); + isSuccess = FileUtil.mergeFile(getEntity().getFilePath(), partPath); } if (isSuccess) { // 合并成功,删除缓存文件 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java similarity index 53% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveUtil.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index 45fa8f5b..29581bc3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -13,17 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8.live; import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.IDownloadListener; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; +import com.arialyy.aria.m3u8.M3U8InfoThread; +import com.arialyy.aria.m3u8.M3U8Listener; +import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.util.ALog; import java.util.ArrayList; import java.util.List; @@ -41,42 +46,65 @@ import java.util.concurrent.TimeUnit; * 4、对于直播来说是没有停止的,停止就代表完成 * 5、不处理直播切片下载失败的状态 */ -public class M3U8LiveUtil implements IUtil { +public class M3U8LiveUtil extends AbsNormalLoaderUtil { private final String TAG = "M3U8LiveDownloadUtil"; - private DTaskWrapper mWrapper; - private IDownloadListener mListener; - private boolean isStop = false, isCancel = false; - private M3U8LiveLoader mLoader; private M3U8InfoThread mInfoThread; private ScheduledThreadPoolExecutor mTimer; private ExecutorService mInfoPool = Executors.newCachedThreadPool(); private List mPeerUrls = new ArrayList<>(); + private M3U8TaskOption mM3U8Option; - public M3U8LiveUtil(DTaskWrapper wrapper, IDownloadListener listener) { - mWrapper = wrapper; - mListener = listener; - mLoader = new M3U8LiveLoader(mListener, mWrapper); + protected M3U8LiveUtil(DTaskWrapper wrapper, M3U8Listener listener) { + super(wrapper, listener); + wrapper.generateM3u8Option(M3U8TaskOption.class); + mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); } - @Override public String getKey() { - return mWrapper.getKey(); + @Override protected AbsLoader createLoader() { + return new M3U8LiveLoader((M3U8Listener) getListener(), (DTaskWrapper) getTaskWrapper()); } - @Override public long getFileSize() { - return 0; + @Override protected Runnable createInfoThread() { + return null; } - @Override public long getCurrentLocation() { - return 0; - } + private Runnable createLiveInfoThread(){ + M3U8InfoThread infoThread = + new M3U8InfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String key, CompleteInfo info) { + ALog.d(TAG, "更新直播的m3u8文件"); + } - @Override public boolean isRunning() { - return mLoader.isRunning(); + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + fail(e, needRetry); + } + }); + infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() { + @Override public void onGetPeer(String url) { + if (mPeerUrls.contains(url)) { + return; + } + mPeerUrls.add(url); + ILiveTsUrlConverter converter = mM3U8Option.getLiveTsUrlConverter(); + if (converter != null) { + if (TextUtils.isEmpty(mM3U8Option.getBandWidthUrl())) { + url = converter.convert(((DownloadEntity) getTaskWrapper().getEntity()).getUrl(), url); + } else { + url = converter.convert(mM3U8Option.getBandWidthUrl(), url); + } + } + if (TextUtils.isEmpty(url) || !url.startsWith("http")) { + fail(new M3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false); + return; + } + getLoader().offerPeer(url); + } + }); + return infoThread; } - @Override public void cancel() { - isCancel = true; - mLoader.cancel(); + @Override protected void onCancel() { + super.onCancel(); if (mInfoThread != null) { mInfoThread.setStop(true); } @@ -85,9 +113,8 @@ public class M3U8LiveUtil implements IUtil { /** * 对于直播来说是没有停止的,停止就代表完成 */ - @Override public void stop() { - isStop = true; - mLoader.stop(); + @Override protected void onStop() { + super.onStop(); handleComplete(); } @@ -95,25 +122,20 @@ public class M3U8LiveUtil implements IUtil { if (mInfoThread != null) { mInfoThread.setStop(true); closeTimer(); - if (mWrapper.asM3U8().isMergeFile()) { - if (mLoader.mergeFile()) { - mListener.onComplete(); + if (mM3U8Option.isMergeFile()) { + if (getLoader().mergeFile()) { + getListener().onComplete(); } else { - mListener.onFail(false, new M3U8Exception(TAG, "合并文件失败")); + getListener().onFail(false, new M3U8Exception(TAG, "合并文件失败")); } } else { - mListener.onComplete(); + getListener().onComplete(); } } } - @Override public void start() { - if (isStop || isCancel) { - return; - } - mListener.onPre(); - getLiveInfo(); - mLoader.start(); + @Override protected void onStart() { + super.onStart(); startTimer(); } @@ -121,10 +143,10 @@ public class M3U8LiveUtil implements IUtil { mTimer = new ScheduledThreadPoolExecutor(1); mTimer.scheduleWithFixedDelay(new Runnable() { @Override public void run() { - mInfoThread = (M3U8InfoThread) getLiveInfo(); + mInfoThread = (M3U8InfoThread) createLiveInfoThread(); mInfoPool.execute(mInfoThread); } - }, 0, mWrapper.asM3U8().getLiveUpdateInterval(), TimeUnit.MILLISECONDS); + }, 0, mM3U8Option.getLiveUpdateInterval(), TimeUnit.MILLISECONDS); } private void closeTimer() { @@ -133,48 +155,12 @@ public class M3U8LiveUtil implements IUtil { } } - /** - * 获取直播文件信息 - */ - private Runnable getLiveInfo() { - M3U8InfoThread infoThread = new M3U8InfoThread(mWrapper, new OnFileInfoCallback() { - @Override public void onComplete(String key, CompleteInfo info) { - ALog.d(TAG, "更新直播的m3u8文件"); - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - failDownload(e, needRetry); - } - }); - infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() { - @Override public void onGetPeer(String url) { - if (mPeerUrls.contains(url)) { - return; - } - mPeerUrls.add(url); - ILiveTsUrlConverter converter = mWrapper.asM3U8().getLiveTsUrlConverter(); - if (converter != null) { - if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) { - url = converter.convert(mWrapper.getEntity().getUrl(), url); - } else { - url = converter.convert(mWrapper.asM3U8().getBandWidthUrl(), url); - } - } - if (TextUtils.isEmpty(url) || !url.startsWith("http")) { - failDownload(new M3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false); - return; - } - mLoader.offerPeer(url); - } - }); - return infoThread; + @Override protected void fail(BaseException e, boolean needRetry) { + super.fail(e, needRetry); + handleComplete(); } - private void failDownload(BaseException e, boolean needRetry) { - if (isStop || isCancel) { - return; - } - handleComplete(); - mListener.onFail(needRetry, e); + @Override public M3U8LiveLoader getLoader() { + return (M3U8LiveLoader) super.getLoader(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java similarity index 88% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 0fb68a06..dc688518 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -13,27 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.m3u8.vod; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.SparseArray; -import com.arialyy.aria.core.common.AbsThreadTask; -import com.arialyy.aria.core.common.IThreadState; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.M3U8Listener; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.event.PeerIndexEvent; -import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.m3u8.BaseM3U8Loader; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.m3u8.M3U8Listener; +import com.arialyy.aria.m3u8.M3U8TaskOption; +import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; import java.io.File; @@ -74,16 +78,18 @@ public class M3U8VodLoader extends BaseM3U8Loader { private int mCompleteNum = 0; private ExecutorService mJumpThreadPool; private Thread jumpThread = null; + private M3U8TaskOption mM3U8Option; M3U8VodLoader(M3U8Listener listener, DTaskWrapper wrapper) { super(listener, wrapper); - mFlagQueue = new ArrayBlockingQueue<>(wrapper.asM3U8().getMaxTsQueueNum()); - EXEC_MAX_NUM = wrapper.asM3U8().getMaxTsQueueNum(); + mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); + mFlagQueue = new ArrayBlockingQueue<>(mM3U8Option.getMaxTsQueueNum()); + EXEC_MAX_NUM = mM3U8Option.getMaxTsQueueNum(); mJumpQueue = new ArrayBlockingQueue<>(10); EventMsgUtil.getDefault().register(this); } - @Override protected IThreadState getStateManager(Looper looper) { + @Override protected IThreadState createStateManager(Looper looper) { mManager = new VodStateManager(looper, mRecord, mListener); mStateHandler = new Handler(looper, mManager); return mManager; @@ -151,6 +157,10 @@ public class M3U8VodLoader extends BaseM3U8Loader { th.start(); } + @Override public long getFileSize() { + return getEntity().getFileSize(); + } + /** * 获取线程记录 */ @@ -180,9 +190,9 @@ public class M3U8VodLoader extends BaseM3U8Loader { * 启动线程任务 */ private void addTaskToQueue(ThreadRecord tr) throws InterruptedException { - M3U8ThreadTask task = createThreadTask(mCacheDir, tr, tr.threadId); + ThreadTask task = createThreadTask(mCacheDir, tr, tr.threadId); getTaskList().put(tr.threadId, task); - mEntity.getM3U8Entity().setPeerIndex(tr.threadId); + getEntity().getM3U8Entity().setPeerIndex(tr.threadId); TempFlag flag = startThreadTask(task, tr.threadId); if (flag != null) { mFlagQueue.put(flag); @@ -194,9 +204,8 @@ public class M3U8VodLoader extends BaseM3U8Loader { */ private void initData() { mCacheDir = getCacheDir(); - if (mTaskWrapper.asM3U8().getJumpIndex() != 0) { - mCurrentEvent = - new PeerIndexEvent(mTaskWrapper.getKey(), mTaskWrapper.asM3U8().getJumpIndex()); + if (mM3U8Option.getJumpIndex() != 0) { + mCurrentEvent = new PeerIndexEvent(mTaskWrapper.getKey(), mM3U8Option.getJumpIndex()); resumeTask(); return; } @@ -397,7 +406,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { * * @return 线程唯一id标志 */ - private TempFlag startThreadTask(M3U8ThreadTask task, int peerIndex) { + private TempFlag startThreadTask(ThreadTask task, int peerIndex) { if (isBreak()) { ALog.w(TAG, "任务已停止,启动线程任务失败"); return null; @@ -414,10 +423,10 @@ public class M3U8VodLoader extends BaseM3U8Loader { /** * 配置config */ - private M3U8ThreadTask createThreadTask(String cacheDir, ThreadRecord record, int index) { - SubThreadConfig config = new SubThreadConfig<>(); + private ThreadTask createThreadTask(String cacheDir, ThreadRecord record, int index) { + SubThreadConfig config = new SubThreadConfig(); config.url = record.tsUrl; - config.tempFile = new File(getTsFilePath(cacheDir, record.threadId)); + config.tempFile = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); config.isBlock = mRecord.isBlock; config.isOpenDynamicFile = mRecord.isOpenDynamicFile; config.taskWrapper = mTaskWrapper; @@ -427,7 +436,10 @@ public class M3U8VodLoader extends BaseM3U8Loader { if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile.getPath()); } - return new M3U8ThreadTask(config); + ThreadTask threadTask = new ThreadTask(config); + M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config); + threadTask.setAdapter(adapter); + return threadTask; } /** @@ -482,7 +494,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { switch (msg.what) { case STATE_STOP: stopNum++; - removeSignThread((AbsThreadTask) msg.obj); + removeSignThread((ThreadTask) msg.obj); // 处理跳转位置后,恢复任务 if (isJump && (stopNum == mCurrentFlagSize || mCurrentFlagSize == 0) && !isBreak()) { resumeTask(); @@ -496,7 +508,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { break; case STATE_CANCEL: cancelNum++; - removeSignThread((AbsThreadTask) msg.obj); + removeSignThread((ThreadTask) msg.obj); if (isBreak()) { ALog.d(TAG, String.format("vod任务【%s】取消", mTempFile.getName())); @@ -527,7 +539,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { quitLooper(); } mCompleteNum++; - // 正在切换位置时,切片完成,队列大小减小 + // 正在切换位置时,切片完成,队列减小 if (isJump) { mCurrentFlagSize--; if (mCurrentFlagSize < 0) { @@ -535,7 +547,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } } - removeSignThread((AbsThreadTask) msg.obj); + removeSignThread((ThreadTask) msg.obj); getListener().onPeerComplete(mTaskWrapper.getKey(), msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex); handlerPercent(); @@ -547,7 +559,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { "startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s", startThreadNum, stopNum, cancelNum, failNum, mCompleteNum, mFlagQueue.size())); ALog.d(TAG, String.format("vod任务【%s】完成", mTempFile.getName())); - if (mTaskWrapper.asM3U8().isMergeFile()) { + if (mM3U8Option.isMergeFile()) { if (mergeFile()) { listener.onComplete(); } else { @@ -566,7 +578,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { return true; } - private void removeSignThread(AbsThreadTask threadTask) { + private void removeSignThread(ThreadTask threadTask) { int index = getTaskList().indexOfValue(threadTask); if (index != -1) { getTaskList().removeAt(index); @@ -578,12 +590,12 @@ public class M3U8VodLoader extends BaseM3U8Loader { * 设置进度 */ private void handlerPercent() { - int completeNum = mTaskWrapper.asM3U8().getCompleteNum(); + int completeNum = mM3U8Option.getCompleteNum(); completeNum++; - mTaskWrapper.asM3U8().setCompleteNum(completeNum); + mM3U8Option.setCompleteNum(completeNum); int percent = completeNum * 100 / taskRecord.threadRecords.size(); - mEntity.setPercent(percent); - mEntity.update(); + getEntity().setPercent(percent); + getEntity().update(); } @Override public boolean isFail() { @@ -616,18 +628,18 @@ public class M3U8VodLoader extends BaseM3U8Loader { if (getEntity().getM3U8Entity().isGenerateIndexFile()) { return generateIndexFile(); } - ITsMergeHandler mergeHandler = mTaskWrapper.asM3U8().getMergeHandler(); + ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); String cacheDir = getCacheDir(); List partPath = new ArrayList<>(); for (ThreadRecord tr : taskRecord.threadRecords) { - partPath.add(getTsFilePath(cacheDir, tr.threadId)); + partPath.add(BaseM3U8Loader.getTsFilePath(cacheDir, tr.threadId)); } boolean isSuccess; if (mergeHandler != null) { isSuccess = mergeHandler.merge(getEntity().getM3U8Entity(), partPath); if (mergeHandler.getClass().isAnonymousClass()) { - mTaskWrapper.asM3U8().setMergeHandler(null); + mM3U8Option.setMergeHandler(null); } } else { isSuccess = FileUtil.mergeFile(taskRecord.filePath, partPath); @@ -653,7 +665,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } private static class TempFlag { - AbsThreadTask threadTask; + ThreadTask threadTask; int threadId; } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java new file mode 100644 index 00000000..abb28034 --- /dev/null +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.m3u8.vod; + +import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.exception.M3U8Exception; +import com.arialyy.aria.m3u8.M3U8InfoThread; +import com.arialyy.aria.m3u8.M3U8Listener; +import com.arialyy.aria.m3u8.M3U8TaskOption; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * M3U8点播文件下载工具 + * 工作流程: + * 1、创建一个和文件同父路径并且同名隐藏文件夹 + * 2、将所有m3u8的ts文件下载到该文件夹中 + * 3、完成所有分片下载后,合并ts文件 + * 4、删除该隐藏文件夹 + */ +public class M3U8VodUtil extends AbsNormalLoaderUtil { + + private M3U8Listener mListener; + private List mUrls = new ArrayList<>(); + private M3U8TaskOption mM3U8Option; + + public M3U8VodUtil(DTaskWrapper wrapper, M3U8Listener listener) { + super(wrapper, listener); + wrapper.generateM3u8Option(M3U8TaskOption.class); + mListener = listener; + mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); + } + + @Override protected AbsLoader createLoader() { + return new M3U8VodLoader((M3U8Listener) getListener(), (DTaskWrapper) getTaskWrapper()); + } + + @Override protected Runnable createInfoThread() { + return new M3U8InfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String key, CompleteInfo info) { + IVodTsUrlConverter converter = mM3U8Option.getVodUrlConverter(); + if (converter != null) { + if (TextUtils.isEmpty(mM3U8Option.getBandWidthUrl())) { + mUrls.addAll(converter.convert(getEntity().getUrl(), (List) info.obj)); + } else { + mUrls.addAll( + converter.convert(mM3U8Option.getBandWidthUrl(), (List) info.obj)); + } + } else { + mUrls.addAll((Collection) info.obj); + } + if (mUrls.isEmpty()) { + fail(new M3U8Exception(TAG, "获取地址失败"), false); + return; + } else if (!mUrls.get(0).startsWith("http")) { + fail(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); + return; + } + mM3U8Option.setUrls(mUrls); + if (isStop()) { + getListener().onStop(getEntity().getCurrentProgress()); + } else if (isCancel()) { + getListener().onCancel(); + } else { + getLoader().start(); + } + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + fail(e, needRetry); + } + }); + } + + private DownloadEntity getEntity() { + return (DownloadEntity) getTaskWrapper().getEntity(); + } +} diff --git a/M3U8Component/src/main/res/values/strings.xml b/M3U8Component/src/main/res/values/strings.xml new file mode 100644 index 00000000..bd8f4556 --- /dev/null +++ b/M3U8Component/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + M3U8Component + diff --git a/PublicComponent/.gitignore b/PublicComponent/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/PublicComponent/.gitignore @@ -0,0 +1 @@ +/build diff --git a/AriaFtpComponent/build.gradle b/PublicComponent/build.gradle similarity index 89% rename from AriaFtpComponent/build.gradle rename to PublicComponent/build.gradle index 12924738..55c66697 100644 --- a/AriaFtpComponent/build.gradle +++ b/PublicComponent/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' @@ -24,9 +24,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" - testImplementation 'junit:junit:4.12' - implementation project(path: ':AriaFtpPlug') } diff --git a/PublicComponent/consumer-rules.pro b/PublicComponent/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/PublicComponent/proguard-rules.pro b/PublicComponent/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/PublicComponent/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/PublicComponent/src/main/AndroidManifest.xml b/PublicComponent/src/main/AndroidManifest.xml new file mode 100644 index 00000000..14fe4789 --- /dev/null +++ b/PublicComponent/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java new file mode 100644 index 00000000..009c77e7 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Build; +import com.arialyy.aria.core.config.AppConfig; +import com.arialyy.aria.core.config.Configuration; +import com.arialyy.aria.core.config.DGroupConfig; +import com.arialyy.aria.core.config.DownloadConfig; +import com.arialyy.aria.core.config.UploadConfig; +import com.arialyy.aria.core.config.XMLReader; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.io.IOException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXException; + +public class AriaConfig { + private static final String TAG = "AriaConfig"; + + public static final String DOWNLOAD_TEMP_DIR = "/Aria/temp/download/"; + public static final String UPLOAD_TEMP_DIR = "/Aria/temp/upload/"; + + private static volatile AriaConfig Instance; + private static Context APP; + private DownloadConfig mDConfig; + private UploadConfig mUConfig; + private AppConfig mAConfig; + private DGroupConfig mDGConfig; + /** + * 是否已经联网,true 已经联网 + */ + private static boolean isConnectedNet = true; + + private AriaConfig(Context context) { + APP = context.getApplicationContext(); + } + + public static AriaConfig init(Context context) { + if (Instance == null) { + synchronized (AriaConfig.class) { + if (Instance == null) { + Instance = new AriaConfig(context); + Instance.initData(); + } + } + } + return Instance; + } + + public static AriaConfig getInstance() { + if (Instance == null) { + ALog.e(TAG, "请使用init()初始化"); + } + return Instance; + } + + public Context getAPP() { + return APP; + } + + private void initData() { + initConfig(); + regNetCallBack(APP); + } + + public DownloadConfig getDConfig() { + return mDConfig; + } + + public UploadConfig getUConfig() { + return mUConfig; + } + + public AppConfig getAConfig() { + return mAConfig; + } + + public DGroupConfig getDGConfig() { + return mDGConfig; + } + + /** + * 注册网络监听,只有配置了检查网络{@link AppConfig#isNetCheck()}才会注册事件 + */ + private void regNetCallBack(Context context) { + if (!getAConfig().isNetCheck()) { + return; + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return; + } + ConnectivityManager cm = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (cm == null) { + return; + } + + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + NetworkRequest request = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { + + @Override public void onLost(Network network) { + super.onLost(network); + isConnectedNet = false; + ALog.d(TAG, "onLost"); + } + + @Override public void onAvailable(Network network) { + super.onAvailable(network); + ALog.d(TAG, "onAvailable"); + isConnectedNet = true; + } + }); + } + + public boolean isConnectedNet() { + return isConnectedNet; + } + + /** + * 初始化配置文件 + */ + private void initConfig() { + mDConfig = Configuration.getInstance().downloadCfg; + mUConfig = Configuration.getInstance().uploadCfg; + mAConfig = Configuration.getInstance().appCfg; + mDGConfig = Configuration.getInstance().dGroupCfg; + + File xmlFile = new File(APP.getFilesDir().getPath() + Configuration.XML_FILE); + File tempDir = new File(APP.getFilesDir().getPath() + "/temp"); + if (!xmlFile.exists()) { + loadConfig(); + } else { + try { + String md5Code = CommonUtil.getFileMD5(xmlFile); + File file = new File(APP.getFilesDir().getPath() + "/temp.xml"); + if (file.exists()) { + file.delete(); + } + CommonUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"), + file.getPath()); + if (!CommonUtil.checkMD5(md5Code, file) || !Configuration.getInstance().configExists()) { + loadConfig(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + if (tempDir.exists()) { + File newDir = new File(APP.getFilesDir().getPath() + AriaConfig.DOWNLOAD_TEMP_DIR); + newDir.mkdirs(); + tempDir.renameTo(newDir); + } + } + + /** + * 加载配置文件 + */ + private void loadConfig() { + try { + XMLReader helper = new XMLReader(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser parser = factory.newSAXParser(); + parser.parse(APP.getAssets().open("aria_config.xml"), helper); + CommonUtil.createFileFormInputStream(APP.getAssets().open("aria_config.xml"), + APP.getFilesDir().getPath() + Configuration.XML_FILE); + } catch (ParserConfigurationException | IOException | SAXException e) { + ALog.e(TAG, e.toString()); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpUrlEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpUrlEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java index ad5ac3ed..24b8ede4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpUrlEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java @@ -15,9 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.core; -import com.arialyy.aria.core.common.ProtocolType; import java.net.InetAddress; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ProtocolType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/core/common/ProtocolType.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java index 829489a9..d1602bd0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ProtocolType.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core; import androidx.annotation.StringDef; import java.lang.annotation.Retention; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java new file mode 100644 index 00000000..baa2fa5d --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core; + +import com.arialyy.aria.core.inf.IEventHandler; +import com.arialyy.aria.core.inf.IOptionConstant; +import java.util.HashMap; +import java.util.Map; + +/** + * 任务配置参数 + * + * @Author lyy + * @Date 2019-09-10 + */ +public class TaskOptionParams { + + /** + * 普通参数 + */ + private Map params = new HashMap<>(); + + /** + * 事件处理对象 + */ + private Map handler = new HashMap<>(); + + /** + * 设置普通参数 + * + * @param key {@link IOptionConstant} + */ + public TaskOptionParams setParams(String key, Object value) { + params.put(key, value); + return this; + } + + /** + * 设置对象参数 + */ + public TaskOptionParams setObjs(String key, IEventHandler handler) { + this.handler.put(key, handler); + return this; + } + + public Map getParams() { + return params; + } + + public Object getParam(String key){ + return params.get(key); + } + + public IEventHandler getHandler(String key){ + return handler.get(key); + } + + public Map getHandler() { + return handler; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java index 90a03463..035d7127 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/TaskRecord.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.annotation.Ignore; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java b/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java index ec29e4fd..aca2b47f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ThreadRecord.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core; import com.arialyy.aria.orm.DbEntity; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java index f7ea95fd..02eb8105 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.common; import android.os.Parcel; import android.os.Parcelable; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.annotation.Default; import com.arialyy.aria.orm.annotation.Ignore; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsGroupEntity.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsGroupEntity.java index c35381d9..4acd8d81 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsGroupEntity.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.common; import android.os.Parcel; import android.os.Parcelable; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java index 52d531db..d26bce47 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsNormalEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.common; import android.os.Parcel; import android.os.Parcelable; @@ -84,6 +84,8 @@ public abstract class AbsNormalEntity extends AbsEntity implements Parcelable { this.redirectUrl = redirectUrl; } + public abstract String getFilePath(); + public AbsNormalEntity() { } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java new file mode 100644 index 00000000..139a5633 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +import com.arialyy.aria.core.inf.IRecordHandlerAdapter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.CommonUtil; + +/** + * @Author lyy + * @Date 2019-09-19 + */ +public abstract class AbsRecordHandlerAdapter implements IRecordHandlerAdapter { + private AbsTaskWrapper mWrapper; + protected String TAG = CommonUtil.getClassName(getClass()); + + public AbsRecordHandlerAdapter(AbsTaskWrapper wrapper) { + mWrapper = wrapper; + } + + public AbsTaskWrapper getWrapper() { + return mWrapper; + } + + protected AbsNormalEntity getEntity() { + return (AbsNormalEntity) getWrapper().getEntity(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java similarity index 87% rename from Aria/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java index 34a04ed3..f6107d28 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/CompleteInfo.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.common; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; /** * Created by AriaL on 2018/3/3. @@ -27,7 +27,7 @@ public class CompleteInfo { */ public int code; - public AbsTaskWrapper wrapper; + public ITaskWrapper wrapper; public Object obj; @@ -35,7 +35,7 @@ public class CompleteInfo { } - public CompleteInfo(int code, AbsTaskWrapper wrapper) { + public CompleteInfo(int code, ITaskWrapper wrapper) { this.code = code; this.wrapper = wrapper; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java new file mode 100644 index 00000000..bef20c95 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.inf.IRecordHandlerAdapter; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.core.wrapper.RecordWrapper; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.DbDataHelper; +import com.arialyy.aria.util.FileUtil; +import java.io.File; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +/** + * 处理任务记录,分配线程区间 + */ +public class RecordHandler implements IRecordHandler { + private final String TAG = "RecordHandler"; + + @Deprecated private File mConfigFile; + private TaskRecord mTaskRecord; + private AbsTaskWrapper mTaskWrapper; + private AbsNormalEntity mEntity; + private IRecordHandlerAdapter mAdapter; + + public RecordHandler(AbsTaskWrapper wrapper) { + mTaskWrapper = wrapper; + mEntity = (AbsNormalEntity) mTaskWrapper.getEntity(); + } + + public void setAdapter(IRecordHandlerAdapter mAdapter) { + this.mAdapter = mAdapter; + } + + /** + * 获取任务记录,如果任务记录存在,检查任务记录 + * 检查记录 对于分块任务: 子分块不存在或被删除,子线程将重新下载 + * 对于普通任务: 预下载文件不存在,则任务任务呗删除 + * 如果任务记录不存在或线程记录不存在,初始化记录 + * + * @return 任务记录 + */ + @Override + public TaskRecord getRecord() { + mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); + if (mConfigFile.exists()) { + convertDb(); + } else { + mTaskRecord = DbDataHelper.getTaskRecord(getFilePath()); + if (mTaskRecord == null) { + initRecord(true); + } else { + File file = new File(mTaskRecord.filePath); + if (!file.exists()) { + ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); + DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); + initRecord(false); + } else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { + initRecord(false); + } + mAdapter.handlerTaskRecord(mTaskRecord); + } + } + saveRecord(); + return mTaskRecord; + } + + /** + * convertDb 是兼容性代码 从3.4.1开始,线程配置信息将存储在数据库中。 将配置文件的内容复制到数据库中,并将配置文件删除 + */ + private void convertDb() { + List records = + DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", + getFilePath()); + if (records == null || records.size() == 0) { + Properties pro = FileUtil.loadConfig(mConfigFile); + if (pro.isEmpty()) { + ALog.d(TAG, "老版本的线程记录为空,任务为新任务"); + initRecord(true); + return; + } + + Set keys = pro.keySet(); + // 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录... + // 第一步应该是record 和 state去重取正确的线程数 + Set set = new HashSet<>(); + for (Object key : keys) { + String str = String.valueOf(key); + int i = Integer.parseInt(str.substring(str.length() - 1)); + set.add(i); + } + int threadNum = set.size(); + if (threadNum == 0) { + ALog.d(TAG, "线程数为空,任务为新任务"); + initRecord(true); + return; + } + mTaskWrapper.setNewTask(false); + mTaskRecord = mAdapter.createTaskRecord(threadNum); + mTaskRecord.isOpenDynamicFile = false; + mTaskRecord.isBlock = false; + File tempFile = new File(getFilePath()); + for (int i = 0; i < threadNum; i++) { + ThreadRecord tRecord = new ThreadRecord(); + tRecord.taskKey = mTaskRecord.filePath; + Object state = pro.getProperty(tempFile.getName() + STATE + i); + Object record = pro.getProperty(tempFile.getName() + RECORD + i); + if (state != null && Integer.parseInt(String.valueOf(state)) == 1) { + tRecord.isComplete = true; + continue; + } + if (record != null) { + long temp = Long.parseLong(String.valueOf(record)); + tRecord.startLocation = temp > 0 ? temp : 0; + } else { + tRecord.startLocation = 0; + } + mTaskRecord.threadRecords.add(tRecord); + } + mConfigFile.delete(); + } + } + + /** + * 初始化任务记录,分配线程区间,如果任务记录不存在,则创建新的任务记录 + * + * @param newRecord {@code true} 需要创建新{@link TaskRecord} + */ + private void initRecord(boolean newRecord) { + if (newRecord) { + mTaskRecord = mAdapter.createTaskRecord(mAdapter.initTaskThreadNum()); + } + mTaskWrapper.setNewTask(true); + int requestType = mTaskWrapper.getRequestType(); + if (requestType == ITaskWrapper.M3U8_LIVE) { + return; + } + long blockSize = mEntity.getFileSize() / mTaskRecord.threadNum; + // 处理线程区间记录 + for (int i = 0; i < mTaskRecord.threadNum; i++) { + long startL = i * blockSize, endL = (i + 1) * blockSize; + ThreadRecord tr = mAdapter.createThreadRecord(mTaskRecord, i, startL, endL); + mTaskRecord.threadRecords.add(tr); + } + } + + /** + * 保存任务记录 + */ + private void saveRecord() { + mTaskRecord.threadNum = mTaskRecord.threadRecords.size(); + mTaskRecord.save(); + if (mTaskRecord.threadRecords != null && !mTaskRecord.threadRecords.isEmpty()) { + DbEntity.saveAll(mTaskRecord.threadRecords); + } + ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size())); + } + + /** + * 获取记录类型 + * + * @return {@link #TYPE_DOWNLOAD}、{@link #TYPE_UPLOAD} + */ + private int getRecordType() { + if (mEntity instanceof DownloadEntity) { + return TYPE_DOWNLOAD; + } else { + return TYPE_UPLOAD; + } + } + + /** + * 获取任务路径 + * + * @return 任务文件路径 + */ + private String getFilePath() { + if (mEntity instanceof DownloadEntity) { + return ((DownloadEntity) mTaskWrapper.getEntity()).getFilePath(); + } else { + return ((UploadEntity) mTaskWrapper.getEntity()).getFilePath(); + } + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java new file mode 100644 index 00000000..a3edd328 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ALog; +import java.io.File; + +/** + * 任务记录帮助类,用于处理统一的逻辑 + * + * @Author lyy + * @Date 2019-09-19 + */ +public class RecordHelper { + private String TAG = "RecordHelper"; + + private AbsTaskWrapper mWrapper; + protected TaskRecord mTaskRecord; + + public RecordHelper(AbsTaskWrapper wrapper, TaskRecord record) { + mWrapper = wrapper; + mTaskRecord = record; + } + + /** + * 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度 + */ + public void handleBlockRecord() { + // 默认线程分块长度 + long normalRectLen = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size(); + for (ThreadRecord tr : mTaskRecord.threadRecords) { + long threadRect = tr.blockLen; + + File temp = + new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, tr.threadId)); + if (!temp.exists()) { + ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath())); + tr.isComplete = false; + tr.startLocation = tr.threadId * normalRectLen; + } else { + if (!tr.isComplete) { + ALog.i(TAG, String.format( + "startLocation = %s; endLocation = %s; block = %s; tempLen = %s; threadId = %s", + tr.startLocation, tr.endLocation, threadRect, temp.length(), tr.threadId)); + + long blockFileLen = temp.length(); // 磁盘中的分块文件长度 + /* + * 检查磁盘中的分块文件 + */ + if (blockFileLen > threadRect) { + ALog.i(TAG, String.format("分块【%s】错误,分块长度【%s】 > 线程区间长度【%s】,将重新开始该分块", + tr.threadId, blockFileLen, threadRect)); + temp.delete(); + tr.startLocation = tr.threadId * threadRect; + continue; + } + + long realLocation = + tr.threadId * normalRectLen + blockFileLen; //正常情况下,该线程的startLocation的位置 + /* + * 检查记录文件 + */ + if (blockFileLen == threadRect) { + ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath())); + tr.startLocation = blockFileLen; + tr.isComplete = true; + } else if (tr.startLocation != realLocation) { // 处理记录小于分块文件长度的情况 + ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation)); + tr.startLocation = realLocation; + } + } else { + ALog.i(TAG, String.format("分块【%s】已完成", temp.getPath())); + } + } + } + mWrapper.setNewTask(false); + } + + /** + * 处理单线程的任务的记录 + */ + public void handleSingleThreadRecord() { + File file = new File(mTaskRecord.filePath); + ThreadRecord tr = mTaskRecord.threadRecords.get(0); + if (!file.exists()) { + ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); + tr.startLocation = 0; + tr.isComplete = false; + tr.endLocation = mWrapper.getEntity().getFileSize(); + } else if (mTaskRecord.isOpenDynamicFile) { + if (file.length() > mWrapper.getEntity().getFileSize()) { + ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); + file.delete(); + tr.startLocation = 0; + tr.isComplete = false; + tr.endLocation = mWrapper.getEntity().getFileSize(); + } else if (file.length() == mWrapper.getEntity().getFileSize()) { + tr.isComplete = true; + } else { + if (file.length() != tr.startLocation) { + ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length())); + tr.startLocation = file.length(); + tr.isComplete = false; + } + } + } + mWrapper.setNewTask(false); + } + + /** + * 处理不支持断点的记录 + */ + public void handleNoSupportBPRecord() { + ThreadRecord tr = mTaskRecord.threadRecords.get(0); + tr.startLocation = 0; + tr.endLocation = mWrapper.getEntity().getFileSize(); + tr.taskKey = mTaskRecord.filePath; + tr.blockLen = tr.endLocation; + tr.isComplete = false; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/RequestEnum.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/common/RequestEnum.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index 11cf9e52..3964916d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -16,15 +16,16 @@ package com.arialyy.aria.core.common; import android.os.Handler; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import java.io.File; /** * 子线程下载信息类 */ -public class SubThreadConfig { +public class SubThreadConfig { - public TASK_WRAPPER taskWrapper; + public AbsTaskWrapper taskWrapper; public boolean isBlock = false; // 启动的线程 public int startThreadNum; diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/AppConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/AppConfig.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/config/AppConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/AppConfig.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/BaseConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseConfig.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/config/BaseConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseConfig.java index c30791ed..a1f63f8f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/BaseConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseConfig.java @@ -1,7 +1,7 @@ package com.arialyy.aria.core.config; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; import java.io.Serializable; @@ -24,7 +24,7 @@ abstract class BaseConfig implements Serializable { * 保存配置 */ void save() { - String basePath = AriaManager.getInstance().getAPP().getFilesDir().getPath(); + String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath(); String path = null; switch (getType()) { case TYPE_DOWNLOAD: diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java index 8d912618..5e33a485 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/BaseTaskConfig.java @@ -16,15 +16,15 @@ package com.arialyy.aria.core.config; -import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; import java.io.Serializable; /** * 通用任务配置 */ public abstract class BaseTaskConfig extends BaseConfig implements Serializable { - + protected String TAG = CommonUtil.getClassName(getClass()); /** * 设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢 @@ -65,8 +65,6 @@ public abstract class BaseTaskConfig extends BaseConfig implements Serializable /** * 执行队列类型 - * - * @see QueueMod */ String queueMod = "wait"; diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/ConfigType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/config/ConfigType.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/Configuration.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/Configuration.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/config/Configuration.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/Configuration.java index 79c46604..2978e092 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/Configuration.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/Configuration.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.config; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.util.FileUtil; import java.io.File; @@ -37,7 +37,7 @@ public final class Configuration { private Configuration() { //删除老版本的配置文件 - String basePath = AriaManager.getInstance().getAPP().getFilesDir().getPath(); + String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath(); del351Config(basePath); File newDCfg = new File(String.format("%s%s", basePath, DOWNLOAD_CONFIG_FILE)); File newUCfg = new File(String.format("%s%s", basePath, UPLOAD_CONFIG_FILE)); @@ -89,7 +89,7 @@ public final class Configuration { * @return {@code true}配置存在,{@code false}配置不存在 */ public boolean configExists() { - String basePath = AriaManager.getInstance().getAPP().getFilesDir().getPath(); + String basePath = AriaConfig.getInstance().getAPP().getFilesDir().getPath(); return (new File(String.format("%s%s", basePath, DOWNLOAD_CONFIG_FILE))).exists() && (new File(String.format("%s%s", basePath, UPLOAD_CONFIG_FILE))).exists() && (new File(String.format("%s%s", basePath, APP_CONFIG_FILE))).exists() diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java similarity index 88% rename from Aria/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java index 5d63a34b..17989d3c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/DGroupConfig.java @@ -15,9 +15,10 @@ */ package com.arialyy.aria.core.config; +import com.arialyy.aria.core.event.DGMaxNumEvent; +import com.arialyy.aria.core.event.DSpeedEvent; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.event.SpeedEvent; -import com.arialyy.aria.core.queue.DownloadGroupTaskQueue; +import com.arialyy.aria.util.ALog; import java.io.Serializable; /** @@ -52,7 +53,7 @@ public class DGroupConfig extends BaseTaskConfig implements Serializable { @Override public DGroupConfig setMaxSpeed(int maxSpeed) { super.setMaxSpeed(maxSpeed); - EventMsgUtil.getDefault().post(new SpeedEvent(maxSpeed)); + EventMsgUtil.getDefault().post(new DSpeedEvent(maxSpeed)); return this; } @@ -77,8 +78,12 @@ public class DGroupConfig extends BaseTaskConfig implements Serializable { } public DGroupConfig setMaxTaskNum(int maxTaskNum) { + if (maxTaskNum <= 0) { + ALog.e(TAG, "组合任务最大任务数不能小于0"); + return this; + } super.setMaxTaskNum(maxTaskNum); - DownloadGroupTaskQueue.getInstance().setMaxTaskNum(maxTaskNum); + EventMsgUtil.getDefault().post(new DGMaxNumEvent(maxTaskNum)); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java index 2a4a300f..21114cce 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/DownloadConfig.java @@ -15,9 +15,10 @@ */ package com.arialyy.aria.core.config; +import com.arialyy.aria.core.event.DMaxNumEvent; +import com.arialyy.aria.core.event.DSpeedEvent; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.event.SpeedEvent; -import com.arialyy.aria.core.queue.DownloadTaskQueue; +import com.arialyy.aria.util.ALog; import java.io.Serializable; /** @@ -49,7 +50,7 @@ public class DownloadConfig extends BaseTaskConfig implements Serializable { @Override public DownloadConfig setMaxSpeed(int maxSpeed) { super.setMaxSpeed(maxSpeed); - EventMsgUtil.getDefault().post(new SpeedEvent(maxSpeed)); + EventMsgUtil.getDefault().post(new DSpeedEvent(maxSpeed)); return this; } @@ -60,8 +61,12 @@ public class DownloadConfig extends BaseTaskConfig implements Serializable { } public DownloadConfig setMaxTaskNum(int maxTaskNum) { + if (maxTaskNum <= 0) { + ALog.e(TAG, "下载任务最大任务数不能小于0"); + return this; + } super.setMaxTaskNum(maxTaskNum); - DownloadTaskQueue.getInstance().setMaxTaskNum(maxTaskNum); + EventMsgUtil.getDefault().post(new DMaxNumEvent(maxTaskNum)); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/TTaskConfigAdapeter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/TTaskConfigAdapeter.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/config/TTaskConfigAdapeter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/TTaskConfigAdapeter.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/UploadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/UploadConfig.java similarity index 75% rename from Aria/src/main/java/com/arialyy/aria/core/config/UploadConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/UploadConfig.java index 3c4996c0..da2108d8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/config/UploadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/UploadConfig.java @@ -16,8 +16,9 @@ package com.arialyy.aria.core.config; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.event.SpeedEvent; -import com.arialyy.aria.core.queue.UploadTaskQueue; +import com.arialyy.aria.core.event.UMaxNumEvent; +import com.arialyy.aria.core.event.USpeedEvent; +import com.arialyy.aria.util.ALog; import java.io.Serializable; /** @@ -30,13 +31,17 @@ public class UploadConfig extends BaseTaskConfig implements Serializable { @Override public UploadConfig setMaxSpeed(int maxSpeed) { super.setMaxSpeed(maxSpeed); - EventMsgUtil.getDefault().post(new SpeedEvent(maxSpeed)); + EventMsgUtil.getDefault().post(new USpeedEvent(maxSpeed)); return this; } public UploadConfig setMaxTaskNum(int maxTaskNum) { + if (maxTaskNum <= 0){ + ALog.e(TAG, "上传任务最大任务数不能小于0"); + return this; + } super.setMaxTaskNum(maxTaskNum); - UploadTaskQueue.getInstance().setMaxTaskNum(maxTaskNum); + EventMsgUtil.getDefault().post(new UMaxNumEvent(maxTaskNum)); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/config/XMLReader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/config/XMLReader.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java index fe6f199c..e1d222ca 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.download; -import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.event.ErrorEvent; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import java.util.List; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java index 936b0964..0e63a544 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DGEntityWrapper.java @@ -15,6 +15,8 @@ */ package com.arialyy.aria.core.download; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.orm.AbsDbWrapper; import com.arialyy.aria.orm.annotation.Many; import com.arialyy.aria.orm.annotation.One; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java index 9b4ca29e..2df6b1fd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DGTaskWrapper.java @@ -17,7 +17,6 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.DGroupConfig; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; import java.util.ArrayList; import java.util.List; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java similarity index 80% rename from Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java index 8ee29ab7..64edb7d6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskWrapper.java @@ -15,11 +15,12 @@ */ package com.arialyy.aria.core.download; +import com.arialyy.aria.core.TaskOptionParams; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.DownloadConfig; -import com.arialyy.aria.core.download.m3u8.M3U8TaskConfig; -import com.arialyy.aria.core.download.tcp.TcpTaskConfig; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.inf.ITaskOption; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ComponentUtil; /** * Created by lyy on 2017/1/23. 下载任务实体和下载实体为一对一关系,下载实体删除,任务实体自动删除 @@ -39,12 +40,9 @@ public class DTaskWrapper extends AbsTaskWrapper { /** * M3u8任务配置信息 */ - private M3U8TaskConfig m3u8TaskConfig; + private ITaskOption m3u8Option; - /** - * TCP任务配置信息 - */ - private TcpTaskConfig tcpTaskConfig; + private TaskOptionParams m3u8Params = new TaskOptionParams(); /** * 文件下载url的临时保存变量 @@ -65,18 +63,19 @@ public class DTaskWrapper extends AbsTaskWrapper { super(entity); } - public M3U8TaskConfig asM3U8() { - if (m3u8TaskConfig == null) { - m3u8TaskConfig = new M3U8TaskConfig(); - } - return m3u8TaskConfig; + public ITaskOption getM3u8Option() { + return m3u8Option; + } + + public void generateM3u8Option(Class clazz) { + m3u8Option = ComponentUtil.getInstance().buildTaskOption(clazz, m3u8Params); } - public TcpTaskConfig asTcp() { - if (tcpTaskConfig == null) { - tcpTaskConfig = new TcpTaskConfig(); + public TaskOptionParams getM3U8Params() { + if (m3u8Params == null) { + m3u8Params = new TaskOptionParams(); } - return tcpTaskConfig; + return m3u8Params; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index e8ebfeaa..168cd70a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -19,9 +19,8 @@ package com.arialyy.aria.core.download; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.annotation.Ignore; import com.arialyy.aria.orm.annotation.Unique; @@ -137,6 +136,7 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { return downloadPath; } + @Override public String getFilePath() { return downloadPath; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index 97662415..ce95a3e9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -16,8 +16,8 @@ package com.arialyy.aria.core.download; import android.os.Parcel; -import com.arialyy.aria.core.inf.AbsGroupEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.common.AbsGroupEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Ignore; import java.util.ArrayList; import java.util.List; @@ -48,7 +48,7 @@ public class DownloadGroupEntity extends AbsGroupEntity { } @Override public int getTaskType() { - return getKey().startsWith("ftp") ? AbsTaskWrapper.D_FTP_DIR : AbsTaskWrapper.DG_HTTP; + return getKey().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR : ITaskWrapper.DG_HTTP; } public DownloadGroupEntity() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Entity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Entity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java index 715d62df..05a01a41 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Entity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.core.download; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.DbDataHelper; @@ -130,7 +130,7 @@ public class M3U8Entity extends DbEntity implements Parcelable { /** * 获取m3u8切片 * 如果任务未完成,则返回所有已下载完成的切片; - * 如果任务已完成,如果你设置{@link M3U8Delegate#merge(boolean)}合并分块的请求,返回null;如果没有设置该请求,则返回所有已下载完成的切片 + * 如果任务已完成,如果你设置了合并分块的请求,返回null;如果没有设置该请求,则返回所有已下载完成的切片 */ public List getCompletedPeer() { if (TextUtils.isEmpty(getCacheDir())) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DGMaxNumEvent.java similarity index 63% rename from Aria/src/main/java/com/arialyy/aria/core/common/AEvent.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/DGMaxNumEvent.java index f5852b92..ac5f9286 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AEvent.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DGMaxNumEvent.java @@ -13,25 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.event; /** - * Aria事件总线 + * 组合任务最大下载任务数事件 */ -public class AEvent { - private static final Object LOCK = new Object(); - private static volatile AEvent INSTANCE = null; +public class DGMaxNumEvent { - private AEvent() { + public int maxNum; - } - - public static AEvent getInstance() { - if (INSTANCE == null) { - synchronized (LOCK) { - INSTANCE = new AEvent(); - } - } - return INSTANCE; + public DGMaxNumEvent(int maxNum) { + this.maxNum = maxNum; } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/event/DMaxNumEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DMaxNumEvent.java new file mode 100644 index 00000000..ef1adb02 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DMaxNumEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.event; + +/** + * 最大下载任务数事件 + */ +public class DMaxNumEvent { + + public int maxNum; + + public DMaxNumEvent(int maxNum) { + this.maxNum = maxNum; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/SpeedEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DSpeedEvent.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/event/SpeedEvent.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/DSpeedEvent.java index 8feb7f59..4b2e30cb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/event/SpeedEvent.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/event/DSpeedEvent.java @@ -15,11 +15,11 @@ */ package com.arialyy.aria.core.event; -public class SpeedEvent { +public class DSpeedEvent { public int speed; - public SpeedEvent(int speed) { + public DSpeedEvent(int speed) { this.speed = speed; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/ErrorEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/ErrorEvent.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/event/ErrorEvent.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/ErrorEvent.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/Event.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/Event.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/event/Event.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/Event.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/EventMethodInfo.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/EventMethodInfo.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/event/EventMethodInfo.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/EventMethodInfo.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/EventMsgUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/EventMsgUtil.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/event/EventMsgUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/EventMsgUtil.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/event/PeerIndexEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/PeerIndexEvent.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/event/PeerIndexEvent.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/event/PeerIndexEvent.java diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/event/UMaxNumEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/UMaxNumEvent.java new file mode 100644 index 00000000..6c59f59e --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/event/UMaxNumEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.event; + +/** + * 最大上传任务数事件 + */ +public class UMaxNumEvent { + + public int maxNum; + + public UMaxNumEvent(int maxNum) { + this.maxNum = maxNum; + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/event/USpeedEvent.java b/PublicComponent/src/main/java/com/arialyy/aria/core/event/USpeedEvent.java new file mode 100644 index 00000000..c3693709 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/event/USpeedEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.event; + +public class USpeedEvent { + + public int speed; + + public USpeedEvent(int speed) { + this.speed = speed; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java similarity index 80% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index a0335b02..8a66e24d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; import android.os.Handler; import android.os.Looper; -import com.arialyy.aria.core.common.IUtil; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.listener.IDGroupListener; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -32,16 +34,8 @@ import java.util.concurrent.TimeUnit; * Created by AriaL on 2017/6/30. * 任务组核心逻辑 */ -public abstract class AbsGroupUtil implements IUtil { - private final String TAG = "AbsGroupUtil"; - /** - * FTP文件夹 - */ - int FTP_DIR = 0xa1; - /** - * D_HTTP 任务组 - */ - int HTTP_GROUP = 0xa2; +public abstract class AbsGroupUtil implements IUtil, Runnable { + protected final String TAG = CommonUtil.getClassName(getClass()); private long mCurrentLocation = 0; protected IDGroupListener mListener; @@ -50,25 +44,44 @@ public abstract class AbsGroupUtil implements IUtil { private boolean isStop = false, isCancel = false; private Handler mScheduler; private SimpleSubQueue mSubQueue = SimpleSubQueue.newInstance(); - private Map mExeLoader = new WeakHashMap<>(); + private Map mExeLoader = new WeakHashMap<>(); private Map mCache = new WeakHashMap<>(); - DGTaskWrapper mGTWrapper; - GroupRunState mState; + private DGTaskWrapper mGTWrapper; + private GroupRunState mState; - AbsGroupUtil(IDGroupListener listener, DGTaskWrapper groupWrapper) { + protected AbsGroupUtil(IDGroupListener listener, DGTaskWrapper groupWrapper) { mListener = listener; mGTWrapper = groupWrapper; mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval(); + mState = new GroupRunState(groupWrapper.getKey(), mListener, + groupWrapper.getSubTaskWrapper().size(), mSubQueue); + mScheduler = new Handler(Looper.getMainLooper(), SimpleSchedulers.newInstance(mState)); + initState(); + } + + /** + * 创建子任务加载器工具 + * + * @param needGetFileInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 + */ + protected abstract AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo); + + protected DGTaskWrapper getWrapper() { + return mGTWrapper; + } + + protected GroupRunState getState() { + return mState; + } + + public Handler getScheduler() { + return mScheduler; } /** * 初始化组合任务状态 */ - void initState() { - mState = - new GroupRunState(mGTWrapper.getKey(), mListener, mGTWrapper.getSubTaskWrapper().size(), - mSubQueue); - mScheduler = new Handler(Looper.getMainLooper(), SimpleSchedulers.newInstance(mState)); + protected void initState() { for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) { mState.updateCompleteNum(); @@ -85,13 +98,6 @@ public abstract class AbsGroupUtil implements IUtil { return mGTWrapper.getKey(); } - /** - * 获取任务类型 - * - * @return {@link #FTP_DIR}、{@link #HTTP_GROUP} - */ - abstract int getTaskType(); - /** * 启动子任务下载 * @@ -102,7 +108,7 @@ public abstract class AbsGroupUtil implements IUtil { if (!mState.isRunning) { startTimer(); } - SubDLoadUtil d = getDownloader(url); + AbsSubDLoadUtil d = getDownloader(url); if (d != null && !d.isRunning()) { mSubQueue.startTask(d); } @@ -115,7 +121,7 @@ public abstract class AbsGroupUtil implements IUtil { */ public void stopSubTask(String url) { if (!checkSubTask(url, "停止")) return; - SubDLoadUtil d = getDownloader(url); + AbsSubDLoadUtil d = getDownloader(url); if (d != null && d.isRunning()) { mSubQueue.stopTask(d); } @@ -147,10 +153,10 @@ public abstract class AbsGroupUtil implements IUtil { * * @param url 子任务下载地址 */ - private SubDLoadUtil getDownloader(String url) { - SubDLoadUtil d = mExeLoader.get(url); + private AbsSubDLoadUtil getDownloader(String url) { + AbsSubDLoadUtil d = mExeLoader.get(url); if (d == null) { - return createAndStartSubLoader(mCache.get(url)); + return createSubLoader(mCache.get(url), true); } return d; } @@ -164,7 +170,7 @@ public abstract class AbsGroupUtil implements IUtil { } @Override public boolean isRunning() { - return mState != null && mState.isRunning; + return mState.isRunning; } @Override public void cancel() { @@ -203,11 +209,15 @@ public abstract class AbsGroupUtil implements IUtil { } @Override public void start() { + new Thread(this).start(); + } + + @Override public void run() { if (isStop || isCancel) { closeTimer(); return; } - if (onPreStart()) { + if (onStart()) { startRunningFlow(); } } @@ -217,7 +227,8 @@ public abstract class AbsGroupUtil implements IUtil { * * @return {@code false} 将不再走后续流程,任务介绍 */ - protected boolean onPreStart() { + protected boolean onStart() { + return false; } @@ -266,21 +277,10 @@ public abstract class AbsGroupUtil implements IUtil { } /** - * 创建并启动子任务下载器 - */ - SubDLoadUtil createAndStartSubLoader(DTaskWrapper taskWrapper) { - return createAndStartSubLoader(taskWrapper, true); - } - - /** - * 创建并启动子任务下载器 - * - * @param needGetFileInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 + * 启动子任务下载器 */ - SubDLoadUtil createAndStartSubLoader(DTaskWrapper taskWrapper, boolean needGetFileInfo) { - SubDLoadUtil loader = new SubDLoadUtil(mScheduler, taskWrapper, needGetFileInfo); + protected void startSubLoader(AbsSubDLoadUtil loader) { mExeLoader.put(loader.getKey(), loader); mSubQueue.startTask(loader); - return loader; } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java new file mode 100644 index 00000000..2de7fad6 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.group; + +import android.os.Handler; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.util.CommonUtil; + +/** + * 子任务下载器,负责创建 + */ +public abstract class AbsSubDLoadUtil implements IUtil { + protected final String TAG = CommonUtil.getClassName(getClass()); + + private NormalLoader mDLoader; + private DTaskWrapper mWrapper; + private Handler mSchedulers; + private ChildDLoadListener mListener; + private boolean needGetInfo; + + /** + * @param schedulers 调度器 + * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 + */ + protected AbsSubDLoadUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) { + mWrapper = taskWrapper; + mSchedulers = schedulers; + this.needGetInfo = needGetInfo; + mListener = new ChildDLoadListener(mSchedulers, AbsSubDLoadUtil.this); + mDLoader = createLoader(mListener, taskWrapper); + } + + /** + * 创建加载器 + */ + protected abstract NormalLoader createLoader(ChildDLoadListener listener, DTaskWrapper wrapper); + + protected boolean isNeedGetInfo() { + return needGetInfo; + } + + public Handler getSchedulers() { + return mSchedulers; + } + + @Override public String getKey() { + return mWrapper.getKey(); + } + + public DTaskWrapper getWrapper() { + return mWrapper; + } + + public DownloadEntity getEntity() { + return mWrapper.getEntity(); + } + + /** + * 重新开始任务 + */ + void reStart() { + if (mDLoader != null) { + mDLoader.retryTask(); + } + } + + public NormalLoader getDownloader() { + return mDLoader; + } + + @Override public long getFileSize() { + return mDLoader == null ? -1 : mDLoader.getFileSize(); + } + + @Override public long getCurrentLocation() { + return mDLoader == null ? -1 : mDLoader.getCurrentLocation(); + } + + @Override public boolean isRunning() { + return mDLoader != null && mDLoader.isRunning(); + } + + @Override public void cancel() { + if (mDLoader != null && isRunning()) { + mDLoader.cancel(); + } else { + mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); + } + } + + @Override public void stop() { + if (mDLoader != null && isRunning()) { + mDLoader.stop(); + } else { + mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget(); + } + } + + //@Override public void start() { + // if (mWrapper.getRequestType() == ITaskWrapper.D_HTTP) { + // if (needGetInfo) { + // new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() { + // + // @Override public void onComplete(String url, CompleteInfo info) { + // mDLoader = new Downloader(mListener, mWrapper); + // mDLoader.start(); + // } + // + // @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + // mSchedulers.obtainMessage(ISchedulers.FAIL, SubDLoadUtil.this).sendToTarget(); + // } + // })).start(); + // } else { + // mDLoader = new Downloader(mListener, mWrapper); + // mDLoader.start(); + // } + // } else if (mWrapper.getRequestType() == ITaskWrapper.D_FTP) { + // mDLoader = new Downloader(mListener, mWrapper); + // mDLoader.start(); + // } else { + // ALog.w(TAG, String.format("不识别的类型,requestType:%s", mWrapper.getRequestType())); + // } + //} +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/ChildDLoadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/ChildDLoadListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java index 4cd0ec03..c606a78b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/ChildDLoadListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java @@ -13,29 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; import android.os.Handler; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IDownloadListener; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.listener.IDLoadListener; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; /** * 子任务事件监听 */ -class ChildDLoadListener implements IDownloadListener { +public class ChildDLoadListener implements IDLoadListener { private DownloadEntity subEntity; private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度 private long lastSaveTime; private long lastLen; private Handler schedulers; - private SubDLoadUtil loader; + private AbsSubDLoadUtil loader; - ChildDLoadListener(Handler schedulers, SubDLoadUtil loader) { + ChildDLoadListener(Handler schedulers, AbsSubDLoadUtil loader) { this.loader = loader; this.schedulers = schedulers; subEntity = loader.getEntity(); @@ -141,7 +140,7 @@ class ChildDLoadListener implements IDownloadListener { * * @param state {@link ISchedulers} */ - private void sendToTarget(int state, SubDLoadUtil util) { + private void sendToTarget(int state, AbsSubDLoadUtil util) { schedulers.obtainMessage(state, util).sendToTarget(); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupRunState.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java similarity index 79% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/GroupRunState.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java index 75616709..8d68a9a8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/GroupRunState.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.listener.IDGroupListener; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import java.util.HashSet; import java.util.Set; /** * 组合任务执行中的状态信息 */ -class GroupRunState { +public class GroupRunState { /** * 子任务数 */ @@ -79,6 +80,19 @@ class GroupRunState { mGroupHash = groupHash; } + /** + * 组合任务是否正在自行 + * + * @return {@code true}组合任务正在执行 + */ + public boolean isRunning() { + return isRunning; + } + + public void setRunning(boolean running) { + isRunning = running; + } + String getGroupHash() { return mGroupHash; } @@ -86,49 +100,49 @@ class GroupRunState { /** * 获取组合任务子任务数 */ - int getSubSize() { + public int getSubSize() { return mSubSize; } /** * 获取失败的数量 */ - int getFailNum() { + public int getFailNum() { return mFailNum; } /** * 获取停止的数量 */ - int getStopNum() { + public int getStopNum() { return mStopNum; } /** * 获取完成的数量 */ - int getCompleteNum() { + public int getCompleteNum() { return mCompleteNum; } /** * 获取当前组合任务总进度 */ - long getProgress() { + public long getProgress() { return mProgress; } /** * 更新完成的数量,mCompleteNum + 1 */ - void updateCompleteNum() { + public void updateCompleteNum() { mCompleteNum++; } /** * 更新任务进度 */ - void updateProgress(long newProgress) { + public void updateProgress(long newProgress) { this.mProgress = newProgress; } @@ -137,7 +151,7 @@ class GroupRunState { * * @param key {@link AbsTaskWrapper#getKey()} */ - void updateCount(String key) { + public void updateCount(String key) { if (mFailTemp.contains(key)) { mFailTemp.remove(key); mFailNum--; @@ -152,7 +166,7 @@ class GroupRunState { * * @param key {@link AbsTaskWrapper#getKey()} */ - void countStopNum(String key) { + public void countStopNum(String key) { mStopTemp.add(key); mStopNum++; } @@ -162,7 +176,7 @@ class GroupRunState { * * @param key {@link AbsTaskWrapper#getKey()} */ - void countFailNum(String key) { + public void countFailNum(String key) { mFailTemp.add(key); mFailNum++; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/GroupSendParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java similarity index 87% rename from Aria/src/main/java/com/arialyy/aria/core/inf/GroupSendParams.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java index 2a977b9e..82a157ec 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/GroupSendParams.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.group; + +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.task.AbsGroupTask; /** * Created by lyy on 2017/9/8. diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/ISubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/ISubQueue.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java index fab093dd..3019b935 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/ISubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; -import com.arialyy.aria.core.common.AbsFileer; -import com.arialyy.aria.core.common.IUtil; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.inf.IUtil; import com.arialyy.aria.core.config.DGroupConfig; /** * 组合任务子任务队列 * - * @param {@link AbsFileer}下载器 + * @param {@link AbsLoader}下载器 */ interface ISubQueue { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSchedulers.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSchedulers.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java index 59633f6d..f4bbf9df 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSchedulers.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; import android.os.Message; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.config.Configuration; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; -import com.arialyy.aria.core.scheduler.ISchedulers; import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.NetUtils; @@ -48,7 +48,7 @@ class SimpleSchedulers implements ISchedulers { } @Override public boolean handleMessage(Message msg) { - SubDLoadUtil loader = (SubDLoadUtil) msg.obj; + AbsSubDLoadUtil loader = (AbsSubDLoadUtil) msg.obj; switch (msg.what) { case RUNNING: mGState.listener.onSubRunning(loader.getEntity()); @@ -79,7 +79,7 @@ class SimpleSchedulers implements ISchedulers { * 2、stopNum + failNum + completeNum + cacheNum == subSize,则认为组合任务停止 * 3、failNum == subSize,只有全部的子任务都失败了,才能任务组合任务失败 */ - private synchronized void handleFail(final SubDLoadUtil loaderUtil) { + private synchronized void handleFail(final AbsSubDLoadUtil loaderUtil) { Configuration config = Configuration.getInstance(); long interval = config.dGroupCfg.getSubReTryInterval(); @@ -87,7 +87,7 @@ class SimpleSchedulers implements ISchedulers { boolean isNotNetRetry = config.appCfg.isNotNetRetry(); final int reTryNum = num; - if ((!NetUtils.isConnected(AriaManager.getInstance().getAPP()) && !isNotNetRetry) + if ((!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) || loaderUtil.getDownloader() == null // 如果获取不到文件信息,loader为空 || loaderUtil.getEntity().getFailNum() > reTryNum) { mQueue.removeTaskFromExecQ(loaderUtil); @@ -128,7 +128,7 @@ class SimpleSchedulers implements ISchedulers { * 1、所有的子任务已经停止,则认为组合任务停止 * 2、completeNum + failNum + stopNum = subSize,则认为组合任务停止 */ - private synchronized void handleStop(SubDLoadUtil loader) { + private synchronized void handleStop(AbsSubDLoadUtil loader) { mGState.listener.onSubStop(loader.getEntity()); mGState.countStopNum(loader.getKey()); if (mGState.getStopNum() == mGState.getSubSize() @@ -153,7 +153,7 @@ class SimpleSchedulers implements ISchedulers { * GroupRunState#getFailNum()}不为0,则认为组合任务被停止 * 3、只有有缓存的子任务,则任务组合任务没有完成 */ - private synchronized void handleComplete(SubDLoadUtil loader) { + private synchronized void handleComplete(AbsSubDLoadUtil loader) { ALog.d(TAG, String.format("子任务【%s】完成", loader.getEntity().getFileName())); ThreadTaskManager.getInstance().removeTaskThread(loader.getKey()); mGState.listener.onSubComplete(loader.getEntity()); @@ -182,7 +182,7 @@ class SimpleSchedulers implements ISchedulers { if (mQueue.isStopAll()) { return; } - SubDLoadUtil next = mQueue.getNextTask(); + AbsSubDLoadUtil next = mQueue.getNextTask(); if (next != null) { ALog.d(TAG, String.format("启动任务:%s", next.getEntity().getFileName())); mQueue.startTask(next); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java similarity index 81% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSubQueue.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java index f004345b..c4f87b70 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/SimpleSubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.group; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.util.ALog; @@ -28,16 +28,16 @@ import java.util.Set; /** * 组合任务队列,该队列生命周期和{@link AbsGroupUtil}生命周期一致 */ -class SimpleSubQueue implements ISubQueue { +class SimpleSubQueue implements ISubQueue { private static final String TAG = "SimpleSubQueue"; /** * 缓存下载器 */ - private Map mCache = new LinkedHashMap<>(); + private Map mCache = new LinkedHashMap<>(); /** * 执行中的下载器 */ - private Map mExec = new LinkedHashMap<>(); + private Map mExec = new LinkedHashMap<>(); /** * 最大执行任务数 @@ -57,7 +57,7 @@ class SimpleSubQueue implements ISubQueue { return new SimpleSubQueue(); } - Map getExec() { + Map getExec() { return mExec; } @@ -72,11 +72,11 @@ class SimpleSubQueue implements ISubQueue { return isStopAll; } - @Override public void addTask(SubDLoadUtil fileer) { + @Override public void addTask(AbsSubDLoadUtil fileer) { mCache.put(fileer.getKey(), fileer); } - @Override public void startTask(SubDLoadUtil fileer) { + @Override public void startTask(AbsSubDLoadUtil fileer) { if (mExec.size() < mExecSize) { mCache.remove(fileer.getKey()); mExec.put(fileer.getKey(), fileer); @@ -88,7 +88,7 @@ class SimpleSubQueue implements ISubQueue { } } - @Override public void stopTask(SubDLoadUtil fileer) { + @Override public void stopTask(AbsSubDLoadUtil fileer) { fileer.stop(); mExec.remove(fileer.getKey()); } @@ -98,7 +98,7 @@ class SimpleSubQueue implements ISubQueue { ALog.d(TAG, "停止组合任务"); Set keys = mExec.keySet(); for (String key : keys) { - SubDLoadUtil loader = mExec.get(key); + AbsSubDLoadUtil loader = mExec.get(key); if (loader != null) { ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName())); loader.stop(); @@ -122,7 +122,7 @@ class SimpleSubQueue implements ISubQueue { if (oldSize < num) { // 处理队列变小的情况,该情况下将停止队尾任务,并将这些任务添加到缓存队列中 if (mExec.size() > num) { Set keys = mExec.keySet(); - List caches = new ArrayList<>(); + List caches = new ArrayList<>(); int i = 0; for (String key : keys) { if (i > num) { @@ -130,19 +130,19 @@ class SimpleSubQueue implements ISubQueue { } i++; } - Collection temp = mCache.values(); + Collection temp = mCache.values(); mCache.clear(); - for (SubDLoadUtil cache : caches) { + for (AbsSubDLoadUtil cache : caches) { addTask(cache); } - for (SubDLoadUtil t : temp) { + for (AbsSubDLoadUtil t : temp) { addTask(t); } } } else { // 处理队列变大的情况,该情况下将增加任务 if (mExec.size() < num) { for (int i = 0; i < diff; i++) { - SubDLoadUtil next = getNextTask(); + AbsSubDLoadUtil next = getNextTask(); if (next != null) { startTask(next); } else { @@ -153,7 +153,7 @@ class SimpleSubQueue implements ISubQueue { } } - @Override public void removeTaskFromExecQ(SubDLoadUtil fileer) { + @Override public void removeTaskFromExecQ(AbsSubDLoadUtil fileer) { if (mExec.containsKey(fileer.getKey())) { if (fileer.isRunning()) { fileer.stop(); @@ -162,7 +162,7 @@ class SimpleSubQueue implements ISubQueue { } } - @Override public void removeTask(SubDLoadUtil fileer) { + @Override public void removeTask(AbsSubDLoadUtil fileer) { removeTaskFromExecQ(fileer); mCache.remove(fileer.getKey()); } @@ -171,7 +171,7 @@ class SimpleSubQueue implements ISubQueue { ALog.d(TAG, "删除组合任务"); Set keys = mExec.keySet(); for (String key : keys) { - SubDLoadUtil loader = mExec.get(key); + AbsSubDLoadUtil loader = mExec.get(key); if (loader != null) { ALog.d(TAG, String.format("停止子任务:%s", loader.getEntity().getFileName())); loader.cancel(); @@ -179,7 +179,7 @@ class SimpleSubQueue implements ISubQueue { } } - @Override public SubDLoadUtil getNextTask() { + @Override public AbsSubDLoadUtil getNextTask() { Iterator keys = mCache.keySet().iterator(); if (keys.hasNext()) { return mCache.get(keys.next()); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/EventFlag.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/EventFlag.java new file mode 100644 index 00000000..689cb365 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/EventFlag.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface EventFlag { +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEntity.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/inf/IEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEntity.java diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEventHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEventHandler.java new file mode 100644 index 00000000..560e8410 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IEventHandler.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +/** + * 事件处理对象 + * + * @Author lyy + * @Date 2019-09-10 + */ +public interface IEventHandler { +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IOptionConstant.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IOptionConstant.java new file mode 100644 index 00000000..e63ef70b --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IOptionConstant.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +/** + * @Author lyy + * @Date 2019-09-10 + */ +public interface IOptionConstant { + // ftp 任务设置常量 + String ftpUrlEntity = "urlEntity"; + String charSet = "charSet"; + String clientConfig = "clientConfig"; + String uploadInterceptor = "uploadInterceptor"; + + // http + String useServerFileName = "useServerFileName"; + String requestEnum = "requestEnum"; + String fileLenAdapter = "fileLenAdapter"; + String params = "params"; + String formFields = "formFields"; + String headers = "headers"; + String proxy = "proxy"; + + // m3u8 vod + String bandWidth = "bandWidth"; + String cacheDir = "cacheDir"; + String generateIndexFileTemp = "generateIndexFileTemp"; + String bandWidthUrlConverter = "bandWidthUrlConverter"; + String mergeFile = "mergeFile"; + String mergeHandler = "mergeHandler"; + String vodUrlConverter = "vodUrlConverter"; + String maxTsQueueNum = "maxTsQueueNum"; + String jumpIndex = "jumpIndex"; + + // m3u8 live + String liveTsUrlConverter = "liveTsUrlConverter"; + String liveUpdateInterval = "liveUpdateInterval"; +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java new file mode 100644 index 00000000..17e75de3 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +import com.arialyy.aria.core.TaskRecord; + +/** + * @Author lyy + * @Date 2019-09-18 + */ +public interface IRecordHandler { + + int TYPE_DOWNLOAD = 1; + int TYPE_UPLOAD = 2; + int TYPE_M3U8_VOD = 3; + int TYPE_M3U8_LIVE = 4; + + String STATE = "_state_"; + String RECORD = "_record_"; + /** + * 小于1m的文件不启用多线程 + */ + long SUB_LEN = 1024 * 1024; + + /** + * 分块文件路径 + */ + String SUB_PATH = "%s.%s.part"; + + /** + * 获取任务记录 + */ + TaskRecord getRecord(); +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java new file mode 100644 index 00000000..85e75f1b --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.inf; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; + +/** + * 任务记录处理适配器 + * + * @Author lyy + * @Date 2019-09-19 + */ +public interface IRecordHandlerAdapter { + + /** + * 处理任务记录 + */ + void handlerTaskRecord(TaskRecord record); + + /** + * 处理线程任务 + * + * @param record 任务记录 + * @param threadId 线程id + * @param startL 线程开始位置 + * @param endL 线程结束位置 + */ + ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL); + + /** + * 新任务创建任务记录 + */ + TaskRecord createTaskRecord(int threadNum); + + /** + * 配置新任务的线程数 + * + * @return 新任务的线程数 + */ + int initTaskThreadNum(); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/inf/ITaskConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java index 72ab22c4..5929eb8a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java @@ -19,6 +19,6 @@ package com.arialyy.aria.core.inf; * Created by AriaL on 2017/6/29. * 任务信息设置接口 */ -public interface ITaskConfig { +public interface ITaskOption { } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/IThreadState.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/common/IThreadState.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java index 996e6108..b48bbc47 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/IThreadState.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.inf; import android.os.Handler; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/IUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/common/IUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java index 23c6d007..a2f29a92 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/IUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.inf; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/OnFileInfoCallback.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java similarity index 87% rename from Aria/src/main/java/com/arialyy/aria/core/common/OnFileInfoCallback.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java index 88fa77a9..71eccce9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/OnFileInfoCallback.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.inf; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.exception.BaseException; public interface OnFileInfoCallback { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/Suggest.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/Suggest.java similarity index 95% rename from Aria/src/main/java/com/arialyy/aria/core/common/Suggest.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/Suggest.java index b9c56fb6..86cf1466 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/Suggest.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/Suggest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.inf; public interface Suggest { diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java similarity index 73% rename from Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java index fa1ee9a8..fa9b6a51 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/BaseDListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java @@ -13,28 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.listener; import android.os.Handler; - -import com.arialyy.aria.core.common.BaseListener; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IDownloadListener; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; -import java.io.File; /** * 下载监听类 */ -public class BaseDListener extends BaseListener - implements IDownloadListener { +public class BaseDListener extends BaseListener> + implements IDLoadListener { - BaseDListener(DownloadTask task, Handler outHandler) { + public BaseDListener(AbsTask task, Handler outHandler) { super(task, outHandler); } @@ -58,10 +55,10 @@ public class BaseDListener extends BaseListener +class BaseUListener extends BaseListener> implements IUploadListener { - BaseUListener(UploadTask task, Handler outHandler) { + BaseUListener(AbsTask task, Handler outHandler) { super(task, outHandler); } @@ -38,10 +39,10 @@ class BaseUListener extends BaseListener if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) { mEntity.setComplete(false); mEntity.setState(IEntity.STATE_WAIT); - RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD, + RecordUtil.delTaskRecord(mEntity.getFilePath(), IRecordHandler.TYPE_UPLOAD, mTaskWrapper.isRemoveFile(), false); } else { - RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD, + RecordUtil.delTaskRecord(mEntity.getFilePath(), IRecordHandler.TYPE_UPLOAD, mTaskWrapper.isRemoveFile(), true); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java index a5afbe57..6d882148 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.listener; import android.os.Handler; -import com.arialyy.aria.core.common.BaseListener; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.download.group.IDGroupListener; -import com.arialyy.aria.core.inf.GroupSendParams; +import com.arialyy.aria.core.download.DGTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -32,12 +33,12 @@ import com.arialyy.aria.util.RecordUtil; /** * Created by Aria.Lao on 2017/7/20. 任务组下载事件 */ -class DownloadGroupListener +public class DownloadGroupListener extends BaseListener implements IDGroupListener { private GroupSendParams mSeedEntity; - DownloadGroupListener(DownloadGroupTask task, Handler outHandler) { + public DownloadGroupListener(DownloadGroupTask task, Handler outHandler) { super(task, outHandler); mSeedEntity = new GroupSendParams<>(); mSeedEntity.groupTask = task; @@ -125,7 +126,7 @@ class DownloadGroupListener subEntity.setConvertSpeed("0kb/s"); subEntity.setSpeed(0); ALog.i(TAG, String.format("任务【%s】完成,将删除线程任务记录", mEntity.getKey())); - RecordUtil.delTaskRecord(subEntity.getFilePath(), RecordHandler.TYPE_DOWNLOAD, false, false); + RecordUtil.delTaskRecord(subEntity.getFilePath(), IRecordHandler.TYPE_DOWNLOAD, false, false); } subEntity.update(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/group/IDGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/download/group/IDGroupListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java index 918c45f6..eca54860 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/group/IDGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.group; +package com.arialyy.aria.core.listener; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IDownloadListener; +import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.exception.BaseException; /** * Created by Aria.Lao on 2017/7/20. * 下载任务组事件 */ -public interface IDGroupListener extends IDownloadListener { +public interface IDGroupListener extends IDLoadListener { /** * 子任务预处理 diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IDownloadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDLoadListener.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/inf/IDownloadListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDLoadListener.java index d5cec1df..fc93f9e4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IDownloadListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDLoadListener.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.listener; /** * 下载监听 */ -public interface IDownloadListener extends IEventListener { +public interface IDLoadListener extends IEventListener { /** * 预处理完成,准备下载---开始下载之间 diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java index 2691797b..da772820 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IEventListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.listener; import com.arialyy.aria.exception.BaseException; diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/ISchedulers.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/ISchedulers.java index 987e5125..75755d7d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/ISchedulers.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/ISchedulers.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.arialyy.aria.core.scheduler; +package com.arialyy.aria.core.listener; import android.os.Handler; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.upload.UploadEntity; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IUploadListener.java similarity index 94% rename from Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/IUploadListener.java index e6196375..b571f37c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IUploadListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IUploadListener.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.listener; /** * Created by lyy on 2017/2/9. diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java similarity index 93% rename from Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java index c37416c2..2cd1f0db 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.upload; +package com.arialyy.aria.core.listener; -import com.arialyy.aria.core.inf.IUploadListener; import com.arialyy.aria.exception.BaseException; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java similarity index 82% rename from Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java index c6988ceb..c74e8c58 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsFileer.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java @@ -13,14 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.loader; import android.os.Looper; import android.util.SparseArray; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IEventListener; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.manager.ThreadTaskManager; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.io.File; @@ -31,15 +34,13 @@ import java.util.concurrent.TimeUnit; * Created by AriaL on 2017/7/1. * 控制线程任务状态,如:开始,停止,取消,重试 */ -public abstract class AbsFileer> - implements Runnable { +public abstract class AbsLoader implements Runnable { protected final String TAG; protected IEventListener mListener; - protected TASK_WRAPPER mTaskWrapper; - protected ENTITY mEntity; + protected AbsTaskWrapper mTaskWrapper; protected File mTempFile; - private SparseArray mTask = new SparseArray<>(); + private SparseArray mTask = new SparseArray<>(); private ScheduledThreadPoolExecutor mTimer; /** @@ -51,29 +52,38 @@ public abstract class AbsFileer getTaskList() { + public SparseArray getTaskList() { return mTask; } @@ -90,6 +100,11 @@ public abstract class AbsFileer> - extends AbsFileer { +/** + * 单文件 + */ +public class NormalLoader extends AbsLoader { private ThreadStateManager mStateManager; private Handler mStateHandler; protected int mTotalThreadNum; //总线程数 private int mStartThreadNum; //启动的线程数 + private ILoaderAdapter mAdapter; - protected NormalFileer(IEventListener listener, TASK_WRAPPER wrapper) { + public NormalLoader(IEventListener listener, AbsTaskWrapper wrapper) { super(listener, wrapper); + mTempFile = new File(getEntity().getFilePath()); EventMsgUtil.getDefault().register(this); + setUpdateInterval(wrapper.getConfig().getUpdateInterval()); } - /** - * 处理新任务 - * - * @return {@code true}创建新任务成功 - */ - protected abstract boolean handleNewTask(); + public void setAdapter(ILoaderAdapter adapter) { + mAdapter = adapter; + } - /** - * 选择单任务线程的类型 - */ - protected abstract AbsThreadTask selectThreadTask(SubThreadConfig config); + public AbsNormalEntity getEntity() { + return (AbsNormalEntity) mTaskWrapper.getEntity(); + } + + @Override public long getFileSize() { + return getEntity().getFileSize(); + } + + @Override public long getCurrentLocation() { + return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress(); + } + + @Override protected IRecordHandler getRecordHandler(AbsTaskWrapper wrapper) { + return mAdapter.recordHandler(wrapper); + } /** * 设置最大下载/上传速度 @@ -57,7 +76,7 @@ public abstract class NormalFileer 0) { task.setMaxSpeed(maxSpeed / mStartThreadNum); } @@ -71,10 +90,31 @@ public abstract class NormalFileer config = new SubThreadConfig<>(); - config.url = mEntity.isRedirect() ? mEntity.getRedirectUrl() : mEntity.getUrl(); + private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) { + SubThreadConfig config = new SubThreadConfig(); + config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl(); config.tempFile = mRecord.isBlock ? new File( - String.format(RecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) + String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) : mTempFile; config.isBlock = mRecord.isBlock; config.isOpenDynamicFile = mRecord.isOpenDynamicFile; @@ -107,16 +147,18 @@ public abstract class NormalFileer partPath = new ArrayList<>(); for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { - partPath.add(String.format(RecordHandler.SUB_PATH, mTaskRecord.filePath, i)); + partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); } boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath); if (isSuccess) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java similarity index 92% rename from Aria/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java index 7d4ad2d0..75a28cbd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java @@ -16,9 +16,8 @@ package com.arialyy.aria.core.manager; -import com.arialyy.aria.core.common.AbsThreadTask; -import com.arialyy.aria.core.inf.AbsTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.HashSet; @@ -82,9 +81,9 @@ public class ThreadTaskManager { * 启动线程任务 * * @param key 任务对应的key{@link AbsTaskWrapper#getKey()} - * @param threadTask 线程任务{@link AbsThreadTask} + * @param threadTask 线程任务{@link IThreadTask} */ - public void startThread(String key, AbsThreadTask threadTask) { + public void startThread(String key, IThreadTask threadTask) { try { LOCK.tryLock(2, TimeUnit.SECONDS); if (mExePool.isShutdown()) { @@ -111,7 +110,7 @@ public class ThreadTaskManager { /** * 任务是否在执行 * - * @param key 任务的key{@link AbsTask#getKey()} + * @param key 任务的key * @return {@code true} 任务正在运行 */ public boolean taskIsRunning(String key) { @@ -155,7 +154,7 @@ public class ThreadTaskManager { * @param key 任务的key * @param task 线程任务 */ - public boolean removeSingleTaskThread(String key, AbsThreadTask task) { + public boolean removeSingleTaskThread(String key, IThreadTask task) { try { LOCK.tryLock(2, TimeUnit.SECONDS); if (mExePool.isShutdown()) { @@ -195,7 +194,7 @@ public class ThreadTaskManager { * * @param task 线程任务 */ - public void retryThread(AbsThreadTask task) { + public void retryThread(IThreadTask task) { try { LOCK.tryLock(2, TimeUnit.SECONDS); if (mExePool.isShutdown()) { @@ -231,6 +230,6 @@ public class ThreadTaskManager { private class FutureContainer { Future future; - AbsThreadTask threadTask; + IThreadTask threadTask; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpInterceptHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/FtpInterceptHandler.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpInterceptHandler.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/FtpInterceptHandler.java index 53114368..bcf5ef17 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/FtpInterceptHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/FtpInterceptHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.core.processor; import android.text.TextUtils; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IBandWidthUrlConverter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IBandWidthUrlConverter.java similarity index 89% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IBandWidthUrlConverter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/IBandWidthUrlConverter.java index ffbde948..21fab034 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IBandWidthUrlConverter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IBandWidthUrlConverter.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.core.processor; + +import com.arialyy.aria.core.inf.IEventHandler; /** * M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的, * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 */ -public interface IBandWidthUrlConverter { +public interface IBandWidthUrlConverter extends IEventHandler { /** * 转换码率地址为可用的http地址,对于某些服务器,返回的切片信息有可能是相对地址,也可能是处理过的, diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/IFtpUploadInterceptor.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/common/ftp/IFtpUploadInterceptor.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java index 1feddc7d..4d8e6277 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ftp/IFtpUploadInterceptor.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common.ftp; +package com.arialyy.aria.core.processor; +import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.upload.UploadEntity; import java.util.List; @@ -41,7 +42,7 @@ import java.util.List; * * */ -public interface IFtpUploadInterceptor { +public interface IFtpUploadInterceptor extends IEventHandler { /** * 处理拦截事件 diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IHttpFileLenAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IHttpFileLenAdapter.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/inf/IHttpFileLenAdapter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/IHttpFileLenAdapter.java index 55b8c507..f7090a86 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IHttpFileLenAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IHttpFileLenAdapter.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.processor; -import java.io.Serializable; +import com.arialyy.aria.core.inf.IEventHandler; import java.net.URLConnection; import java.util.List; import java.util.Map; @@ -23,8 +23,7 @@ import java.util.Map; /** * Http文件长度适配器 */ -public interface IHttpFileLenAdapter extends Serializable { - long serialVersionUID = 1L; +public interface IHttpFileLenAdapter extends IEventHandler { /** * 同伙header中的数据获取文件长度 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ILiveTsUrlConverter.java similarity index 88% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/ILiveTsUrlConverter.java index 09a6abb8..bfd06ceb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ILiveTsUrlConverter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ILiveTsUrlConverter.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.core.processor; + +import com.arialyy.aria.core.inf.IEventHandler; /** * M3U8 直播下载,ts url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 */ -public interface ILiveTsUrlConverter { +public interface ILiveTsUrlConverter extends IEventHandler { /** * 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java similarity index 85% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java index 35e923d3..5f093806 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/ITsMergeHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.core.processor; import androidx.annotation.Nullable; +import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.inf.IEventHandler; import java.util.List; /** * Ts文件合并处理,如果你希望使用自行处理ts文件的合并,你可以实现该接口 */ -public interface ITsMergeHandler { +public interface ITsMergeHandler extends IEventHandler { /** * 合并ts文件 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IVodTsUrlConverter.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/processor/IVodTsUrlConverter.java index 2ccfc7a6..ff7a7b9b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/IVodTsUrlConverter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IVodTsUrlConverter.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download.m3u8; +package com.arialyy.aria.core.processor; +import com.arialyy.aria.core.inf.IEventHandler; import java.util.List; /** * M3U8 点播文件下载,ts url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 */ -public interface IVodTsUrlConverter { +public interface IVodTsUrlConverter extends IEventHandler { /** * 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接 diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java index 421fe86d..c8f0918e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsGroupTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.task; -import com.arialyy.aria.core.download.group.AbsGroupUtil; +import com.arialyy.aria.core.download.AbsGroupTaskWrapper; +import com.arialyy.aria.core.group.AbsGroupUtil; /** * Created by AriaL on 2017/6/29. diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java new file mode 100644 index 00000000..1515e27a --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.task; + +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.loader.ILoaderAdapter; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; + +/** + * 但文件任务适配器 + * + * @Author lyy + * @Date 2019-09-19 + */ +public abstract class AbsNormalLoaderAdapter implements ILoaderAdapter { + protected String TAG = CommonUtil.getClassName(getClass()); + + private ITaskWrapper mWrapper; + private File mTempFile; + + public AbsNormalLoaderAdapter(ITaskWrapper wrapper) { + mWrapper = wrapper; + mTempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath()); + } + + public ITaskWrapper getWrapper() { + return mWrapper; + } + + public File getTempFile() { + return mTempFile; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java similarity index 86% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java index fc206bd2..514129a6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java @@ -13,12 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.task; import android.content.Context; import android.os.Handler; import android.text.TextUtils; -import com.arialyy.aria.core.common.IUtil; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.TaskSchedulerType; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.HashMap; @@ -30,7 +34,7 @@ import java.util.Map; public abstract class AbsTask implements ITask { public static final String ERROR_INFO_KEY = "ERROR_INFO_KEY"; - + protected String TAG = CommonUtil.getClassName(getClass()); /** * 是否需要重试,默认为false */ @@ -38,7 +42,7 @@ public abstract class AbsTask protected TASK_WRAPPER mTaskWrapper; protected Handler mOutHandler; protected Context mContext; - boolean isHeighestTask = false; + protected boolean isHeighestTask = false; private boolean isCancel = false, isStop = false; private IUtil mUtil; /** @@ -51,10 +55,8 @@ public abstract class AbsTask @TaskSchedulerType private int mSchedulerType = TaskSchedulerType.TYPE_DEFAULT; protected IEventListener mListener; - protected String TAG; protected AbsTask() { - TAG = CommonUtil.getClassName(this); } public Handler getOutHandler() { @@ -92,6 +94,19 @@ public abstract class AbsTask this.needRetry = needRetry; } + /** + * 最高优先级命令,最高优先级命令有以下属性 + * 1、在下载队列中,有且只有一个最高优先级任务 + * 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 + * 3、任务调度器不会暂停最高优先级任务 + * 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 + * 5、如果下载队列中已经满了,则会停止队尾的任务 + * 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 + */ + public void setHighestPriority(boolean isHighestPriority) { + isHeighestTask = isHighestPriority; + } + /** * 读取扩展数据 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java new file mode 100644 index 00000000..8e4efe97 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.task; + +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.config.BaseTaskConfig; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.util.BandwidthLimiter; +import com.arialyy.aria.util.CommonUtil; + +/** + * @Author lyy + * @Date 2019-09-18 + */ +public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { + + protected String TAG = CommonUtil.getClassName(getClass()); + /** + * 速度限制工具 + */ + protected BandwidthLimiter mSpeedBandUtil; + /** + * 当前线程的下去区间的进度 + */ + private long mRangeProgress = 0; + private ThreadRecord mThreadRecord; + private IThreadTaskObserver mObserver; + private AbsTaskWrapper mWrapper; + private SubThreadConfig mThreadConfig; + private IThreadTask mThreadTask; + + protected AbsThreadTaskAdapter(SubThreadConfig config) { + mThreadRecord = config.record; + mWrapper = config.taskWrapper; + mThreadConfig = config; + mRangeProgress = mThreadRecord.startLocation; + if (getTaskConfig().getMaxSpeed() > 0) { + mSpeedBandUtil = new BandwidthLimiter(getTaskConfig().getMaxSpeed(), config.startThreadNum); + } + } + + @Override public void call(IThreadTask threadTask) throws Exception { + mThreadTask = threadTask; + handlerThreadTask(); + } + + /** + * 开始处理线程任务 + */ + protected abstract void handlerThreadTask(); + + /** + * 当前线程的下去区间的进度 + */ + protected long getRangeProgress() { + return mRangeProgress; + } + + protected ThreadRecord getThreadRecord() { + return mThreadRecord; + } + + @Deprecated + protected AbsTaskWrapper getTaskWrapper() { + // TODO: 2019-09-18 需要修改方法名称为:getWrapper + return mWrapper; + } + + /** + * 获取任务配置信息 + */ + protected BaseTaskConfig getTaskConfig() { + return getTaskWrapper().getConfig(); + } + + protected IThreadTask getThreadTask() { + return mThreadTask; + } + + /** + * 获取线程配置信息 + */ + @Deprecated + protected SubThreadConfig getConfig() { + // TODO: 2019-09-18 需要修改方法名称为:getThreadConfig + return mThreadConfig; + } + + @Override public void setThreadStateObserver(IThreadTaskObserver observer) { + mObserver = observer; + } + + @Override public void setMaxSpeed(int speed) { + if (mSpeedBandUtil == null) { + mSpeedBandUtil = + new BandwidthLimiter(getTaskConfig().getMaxSpeed(), getConfig().startThreadNum); + } + mSpeedBandUtil.setMaxRate(speed); + } + + protected void complete() { + if (mObserver != null) { + mObserver.updateCompleteState(); + } + } + + protected void fail(BaseException ex, boolean needRetry) { + if (mObserver != null) { + mObserver.updateFailState(ex, needRetry); + } + } + + protected void progress(long len) { + if (mObserver != null) { + mObserver.updateProgress(len); + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java similarity index 67% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java index 7ad5568c..329e1b3d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadGroupTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.task; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.download.group.DGroupUtil; -import com.arialyy.aria.core.download.group.FtpDirDownloadUtil; -import com.arialyy.aria.core.download.group.IDGroupListener; -import com.arialyy.aria.core.inf.AbsGroupTask; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.download.DGTaskWrapper; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.ComponentUtil; /** * Created by AriaL on 2017/6/27. @@ -37,8 +35,9 @@ public class DownloadGroupTask extends AbsGroupTask { private DownloadGroupTask(DGTaskWrapper taskWrapper, Handler outHandler) { mTaskWrapper = taskWrapper; mOutHandler = outHandler; - mContext = AriaManager.getInstance().getAPP(); - mListener = new DownloadGroupListener(this, mOutHandler); + mContext = AriaConfig.getInstance().getAPP(); + mListener = + ComponentUtil.getInstance().buildListener(taskWrapper.getRequestType(), this, mOutHandler); } public DownloadGroupEntity getEntity() { @@ -55,14 +54,7 @@ public class DownloadGroupTask extends AbsGroupTask { } @Override protected synchronized IUtil createUtil() { - int taskType = mTaskWrapper.getRequestType(); - if (taskType == ITaskWrapper.DG_HTTP) { - return new DGroupUtil((IDGroupListener) mListener, mTaskWrapper); - } else if (taskType == ITaskWrapper.D_FTP_DIR) { - return new FtpDirDownloadUtil((IDGroupListener) mListener, mTaskWrapper); - } else { - throw new IllegalArgumentException(String.format("不识别的任务, 任务类型:%s", taskType)); - } + return ComponentUtil.getInstance().buildUtil(mTaskWrapper, mListener); } public static class Builder { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java similarity index 55% rename from Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java index 685682c6..002fdbff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java @@ -14,48 +14,29 @@ * limitations under the License. */ -package com.arialyy.aria.core.download; +package com.arialyy.aria.core.task; import android.os.Handler; import android.os.Looper; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.IUtil; -import com.arialyy.aria.core.download.downloader.SimpleDownloadUtil; -import com.arialyy.aria.core.download.m3u8.M3U8LiveUtil; -import com.arialyy.aria.core.download.m3u8.M3U8VodUtil; -import com.arialyy.aria.core.inf.AbsNormalTask; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IDownloadListener; -import com.arialyy.aria.core.inf.ITaskWrapper; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.util.ComponentUtil; /** * Created by lyy on 2016/8/11. * 下载任务类 */ -public class DownloadTask extends AbsNormalTask { - public static final String TAG = "DownloadTask"; +public class DownloadTask extends AbsTask { private DownloadTask(DTaskWrapper taskWrapper, Handler outHandler) { mTaskWrapper = taskWrapper; mOutHandler = outHandler; - mContext = AriaManager.getInstance().getAPP(); - if (taskWrapper.getRequestType() == AbsTaskWrapper.M3U8_VOD - || taskWrapper.getRequestType() == AbsTaskWrapper.M3U8_LIVE) { - mListener = new M3U8Listener(this, mOutHandler); - } else { - mListener = new BaseDListener(this, mOutHandler); - } - } - - /** - * 获取文件保存路径 - * - * @deprecated 后续版本将删除该方法,请使用{@link #getFilePath()} - */ - @Deprecated - public String getDownloadPath() { - return getFilePath(); + mContext = AriaConfig.getInstance().getAPP(); + mListener = + ComponentUtil.getInstance().buildListener(taskWrapper.getRequestType(), this, mOutHandler); } /** @@ -79,7 +60,7 @@ public class DownloadTask extends AbsNormalTask { } @Override public int getTaskType() { - return DOWNLOAD; + return ITask.DOWNLOAD; } @Override public String getKey() { @@ -95,16 +76,7 @@ public class DownloadTask extends AbsNormalTask { } @Override protected synchronized IUtil createUtil() { - int taskType = mTaskWrapper.getRequestType(); - if (taskType == ITaskWrapper.M3U8_VOD) { - return new M3U8VodUtil(mTaskWrapper, (M3U8Listener) mListener); - } else if (taskType == ITaskWrapper.M3U8_LIVE) { - return new M3U8LiveUtil(mTaskWrapper, (M3U8Listener) mListener); - } else if (taskType == ITaskWrapper.D_HTTP || taskType == ITaskWrapper.D_FTP) { - return new SimpleDownloadUtil(mTaskWrapper, (IDownloadListener) mListener); - } else { - throw new IllegalArgumentException(String.format("不识别的任务, 任务类型:%s", taskType)); - } + return ComponentUtil.getInstance().buildUtil(mTaskWrapper, mListener); } public static class Builder { diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java similarity index 90% rename from Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java index 2cdc9d61..e9f1e5a4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.task; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.inf.TaskSchedulerType; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by lyy on 2017/2/13. - * 任务接口{@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} */ public interface ITask { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java new file mode 100644 index 00000000..7af9aca5 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.task; + +import java.util.concurrent.Callable; + +/** + * @Author lyy + * @Date 2019-09-18 + */ +public interface IThreadTask extends Callable { + + /** + * 销毁任务 + */ + void destroy(); + + /** + * 判断线程任务是否销毁 + * + * @return true 已经销毁 + */ + boolean isDestroy(); + + /** + * 中断任务 + */ + void breakTask(); + + /** + * 当前线程是否完成,对于不支持断点的任务,一律未完成 + * + * @return {@code true} 完成;{@code false} 未完成 + */ + boolean isThreadComplete(); + + /** + * 取消任务 + */ + void cancel(); + + /** + * 停止任务 + */ + void stop(); + + /** + * 设置当前线程最大下载速度 + * + * @param speed 单位为:kb + */ + void setMaxSpeed(int speed); + + /** + * 线程是否存活 + * + * @return {@code true}存活 + */ + boolean isLive(); + + /** + * 任务是否中断,中断条件: + * 1、任务取消 + * 2、任务停止 + * 3、手动中断 + * + * @return {@code true} 中断,{@code false} 不是中断 + */ + boolean isBreak(); + + /** + * 检查下载完成的分块大小,如果下载完成的分块大小大于或小于分配的大小,则需要重新下载该分块 如果是非分块任务,直接返回{@code true} + * + * @return {@code true} 分块分大小正常,{@code false} 分块大小错误 + */ + boolean checkBlock(); +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java new file mode 100644 index 00000000..f03deb65 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.task; + +/** + * 线程适配器 + * + * @Author lyy + * @Date 2019-09-18 + */ +public interface IThreadTaskAdapter { + + /** + * 执行任务 + */ + void call(IThreadTask threadTask) throws Exception; + + /** + * 设置当前线程最大下载速度 + * + * @param speed 单位为:kb + */ + void setMaxSpeed(int speed); + + /** + * 设置线程任务状态观察者 + */ + void setThreadStateObserver(IThreadTaskObserver observer); +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java new file mode 100644 index 00000000..8a92bcf3 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.task; + +import android.os.Bundle; +import androidx.annotation.Nullable; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.exception.BaseException; + +/** + * 线程任务观察者 + * + * @Author lyy + * @Date 2019-09-18 + */ +public interface IThreadTaskObserver { + + /** + * 更新所有状态 + * + * @param state state {@link IThreadState#STATE_STOP}.. + */ + void updateState(int state, @Nullable Bundle bundle); + + /** + * 更新完成的状态 + */ + void updateCompleteState(); + + /** + * 更新失败的状态 + * + * @param needRetry 是否需要重试,一般是网络错误才需要重试 + */ + void updateFailState(@Nullable BaseException e, boolean needRetry); + + /** + * 更新进度 + * + * @param len 新增的长度 + */ + void updateProgress(long len); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java similarity index 78% rename from Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index f061e8b7..d711559a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.task; import android.net.TrafficStats; import android.os.Bundle; @@ -21,16 +21,15 @@ import android.os.Handler; import android.os.Message; import android.os.Process; import androidx.annotation.Nullable; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.config.BaseTaskConfig; -import com.arialyy.aria.core.config.DGroupConfig; -import com.arialyy.aria.core.config.DownloadConfig; -import com.arialyy.aria.core.config.UploadConfig; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.manager.ThreadTaskManager; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; @@ -39,39 +38,35 @@ import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.NetUtils; import java.io.File; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by lyy on 2017/1/18. 任务线程 */ -public abstract class AbsThreadTask> - implements Callable { +public class ThreadTask implements IThreadTask, IThreadTaskObserver { /** * 线程重试次数 */ private final int RETRY_NUM = 2; - private final String TAG = "AbsThreadTask"; - /** - * 当前子线程相对于总长度的位置 - */ - protected long mChildCurrentLocation = 0; - private ENTITY mEntity; - protected TASK_WRAPPER mTaskWrapper; + private IEntity mEntity; + protected AbsTaskWrapper mTaskWrapper; private int mFailTimes = 0; private long mLastSaveTime; - private ExecutorService mConfigThreadPool; private boolean isNotNetRetry; //断网情况是否重试 private boolean taskBreak = false; //任务跳出 - protected BandwidthLimiter mSpeedBandUtil; //速度限制工具 - protected AriaManager mAridManager; private boolean isDestroy = false; protected boolean isCancel = false, isStop = false; - protected ThreadRecord mRecord; + private ExecutorService mConfigThreadPool; private Handler mStateHandler; - private SubThreadConfig mConfig; + private SubThreadConfig mConfig; + /** + * 当前子线程相对于总长度的位置 + */ + protected long mChildCurrentLocation = 0; + private IThreadTaskAdapter mAdapter; + protected ThreadRecord mRecord; private Thread mConfigThread = new Thread(new Runnable() { @Override public void run() { @@ -81,7 +76,7 @@ public abstract class AbsThreadTask config) { + public ThreadTask(SubThreadConfig config) { mConfig = config; mTaskWrapper = config.taskWrapper; mRecord = config.record; @@ -89,14 +84,18 @@ public abstract class AbsThreadTask 0) { - mSpeedBandUtil = new BandwidthLimiter(getMaxSpeed(), config.startThreadNum); - } - isNotNetRetry = mAridManager.getAppConfig().isNotNetRetry(); + + isNotNetRetry = AriaConfig.getInstance().getAConfig().isNotNetRetry(); mChildCurrentLocation = mRecord.startLocation; } + /** + * 设置线程任务适配器 + */ + public void setAdapter(IThreadTaskAdapter adapter) { + mAdapter = adapter; + } + /** * 当前线程处理的文件名 */ @@ -104,89 +103,71 @@ public abstract class AbsThreadTask getConfig() { + public SubThreadConfig getConfig() { return mConfig; } /** * 设置线程是否中断 */ + @Override public void destroy() { this.isDestroy = true; } - /** - * 线程执行完成 - */ - protected void onThreadComplete() { - - } - /** * 线程是否存活 * * @return {@code true}存活 */ - protected boolean isLive() { + @Override + public boolean isLive() { return !Thread.currentThread().isInterrupted() && !isDestroy; } /** * 当前线程是否完成,对于不支持断点的任务,一律未完成 {@code true} 完成;{@code false} 未完成 */ - boolean isThreadComplete() { + @Override + public boolean isThreadComplete() { return mRecord.isComplete; } /** * 获取实体 */ - protected ENTITY getEntity() { + protected IEntity getEntity() { return mEntity; } /** * 获取任务驱动对象 */ - protected TASK_WRAPPER getTaskWrapper() { + protected ITaskWrapper getTaskWrapper() { return mTaskWrapper; } - /** - * 获取配置的最大上传/下载速度 - * - * @return 单位为:kb - */ - public abstract int getMaxSpeed(); - - /** - * 读取任务配置 - * - * @return {@link DownloadConfig}、{@link UploadConfig}、{@link DGroupConfig} - */ - protected abstract BaseTaskConfig getTaskConfig(); - /** * 设置当前线程最大下载速度 * * @param speed 单位为:kb */ - + @Override public void setMaxSpeed(int speed) { - if (mSpeedBandUtil == null) { - mSpeedBandUtil = new BandwidthLimiter(getMaxSpeed(), getConfig().startThreadNum); + if (mAdapter != null) { + mAdapter.setMaxSpeed(speed); } - mSpeedBandUtil.setMaxRate(speed); } /** * 中断任务 */ - void breakTask() { + @Override + public void breakTask() { taskBreak = true; if (mTaskWrapper.isSupportBP()) { final long currentTemp = mChildCurrentLocation; - sendState(IThreadState.STATE_STOP, null); + updateState(IThreadState.STATE_STOP, null); ALog.d(TAG, String.format("任务【%s】thread__%s__中断【停止位置:%s】", getFileName(), mRecord.threadId, currentTemp)); writeConfig(false, currentTemp); @@ -195,39 +176,7 @@ public abstract class AbsThreadTask getEntity().getFileSize() && !getTaskWrapper().asHttp() - // .isChunked()) { - // String errorMsg = - // String.format("下载失败,下载长度超出文件真实长度;currentLocation=%s, fileSize=%s", - // getState().CURRENT_LOCATION, - // getEntity().getFileSize()); - // taskBreak = true; - // fail(mChildCurrentLocation, new FileException(TAG, errorMsg), false); - // return; - //} + @Override + public synchronized void updateState(int state, @Nullable Bundle bundle) { + Message msg = mStateHandler.obtainMessage(); + msg.what = state; + if (state != IThreadState.STATE_UPDATE_PROGRESS) { + msg.obj = this; + } + + if ((state == IThreadState.STATE_COMPLETE || state == IThreadState.STATE_FAIL) + && (mTaskWrapper.getRequestType() == AbsTaskWrapper.M3U8_VOD + || mTaskWrapper.getRequestType() == AbsTaskWrapper.M3U8_LIVE)) { + if (bundle == null) { + bundle = new Bundle(); + } + bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url); + bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath()); + bundle.putInt(ISchedulers.DATA_M3U8_PEER_INDEX, getConfig().peerIndex); + } + if (bundle != null) { + msg.setData(bundle); + } + Thread loopThread = mStateHandler.getLooper().getThread(); + if (!loopThread.isAlive() || loopThread.isInterrupted()) { + return; + } + msg.sendToTarget(); + } + + @Override public synchronized void updateCompleteState() { + ALog.i(TAG, String.format("任务【%s】线程__%s__下载完毕", getTaskWrapper().getKey(), mRecord.threadId)); + writeConfig(true, mRecord.endLocation); + updateState(IThreadState.STATE_COMPLETE, null); + } + + /** + * 更新失败的状态 + * + * @param needRetry 是否需要重试,一般是网络错误才需要重试 + */ + @Override public void updateFailState(@Nullable BaseException e, boolean needRetry) { + + } + + @Override + public synchronized void updateProgress(long len) { mChildCurrentLocation += len; Thread loopThread = mStateHandler.getLooper().getThread(); if (!loopThread.isAlive() || loopThread.isInterrupted()) { @@ -330,9 +319,10 @@ public abstract class AbsThreadTask { +public class UploadTask extends AbsTask { private UploadTask(UTaskWrapper taskWrapper, Handler outHandler) { mTaskWrapper = taskWrapper; mOutHandler = outHandler; - mListener = new BaseUListener(this, mOutHandler); + mListener = + ComponentUtil.getInstance().buildListener(taskWrapper.getRequestType(), this, mOutHandler); } @Override public int getTaskType() { @@ -51,7 +52,7 @@ public class UploadTask extends AbsNormalTask { } @Override protected synchronized IUtil createUtil() { - return new SimpleUploadUtil(mTaskWrapper, (IUploadListener) mListener); + return ComponentUtil.getInstance().buildUtil(mTaskWrapper, mListener); } public static class Builder { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java index 29bdb811..42e5d4c8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.upload; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.UploadConfig; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by lyy on 2017/2/9. 上传任务实体 diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java similarity index 94% rename from Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index 9cff621f..f7d74df9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -17,8 +17,8 @@ package com.arialyy.aria.core.upload; import android.os.Parcel; import android.os.Parcelable; -import com.arialyy.aria.core.inf.AbsNormalEntity; -import com.arialyy.aria.core.inf.ITaskWrapper; +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Primary; /** @@ -44,6 +44,7 @@ public class UploadEntity extends AbsNormalEntity implements Parcelable { this.responseStr = responseStr; } + @Override public String getFilePath() { return filePath; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java similarity index 77% rename from Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index 5d41213e..b97e8f61 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -13,27 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.wrapper; -import com.arialyy.aria.core.common.ftp.FtpTaskConfig; -import com.arialyy.aria.core.common.http.HttpTaskConfig; +import com.arialyy.aria.core.TaskOptionParams; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.config.BaseTaskConfig; -import com.arialyy.aria.core.config.DGroupConfig; -import com.arialyy.aria.core.config.DownloadConfig; -import com.arialyy.aria.core.config.UploadConfig; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.event.ErrorEvent; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.util.ComponentUtil; /** * Created by lyy on 2017/2/23. 所有任务实体的父类 */ public abstract class AbsTaskWrapper - implements ITaskWrapper { - - private HttpTaskConfig httpTaskConfig; - private FtpTaskConfig ftpTaskConfig; + implements ITaskWrapper { /** * 刷新信息 {@code true} 重新刷新下载信息 @@ -47,7 +44,7 @@ public abstract class AbsTaskWrapper /** * 请求类型 {@link ITaskWrapper#D_HTTP}、{@link ITaskWrapper#D_FTP}、{@link - * ITaskWrapper#D_FTP_DIR}。。。 + * ITaskWrapper#D_FTP_DIR}.. */ private int requestType = D_HTTP; @@ -77,6 +74,31 @@ public abstract class AbsTaskWrapper */ private ErrorEvent errorEvent; + /** + * 任务配置 + */ + private ITaskOption taskOption; + + /** + * 任务配置信息 + */ + private TaskOptionParams optionParams = new TaskOptionParams(); + + public ITaskOption getTaskOption() { + return taskOption; + } + + public void generateTaskOption(Class clazz) { + taskOption = ComponentUtil.getInstance().buildTaskOption(clazz, optionParams); + } + + public TaskOptionParams getOptionParams() { + if (optionParams == null) { + optionParams = new TaskOptionParams(); + } + return optionParams; + } + public AbsTaskWrapper(ENTITY entity) { this.entity = entity; } @@ -89,42 +111,10 @@ public abstract class AbsTaskWrapper this.errorEvent = errorEvent; } - /** - * 任务识别标志 {@link AbsEntity#getKey()} - */ - public abstract String getKey(); - - /** - * 任务配置 - * - * @return {@link DownloadConfig}、{@link UploadConfig}、{@link DGroupConfig} - */ - public abstract BaseTaskConfig getConfig(); - @Override public ENTITY getEntity() { return entity; } - /** - * 设置或获取HTTP任务相关参数 - */ - public HttpTaskConfig asHttp() { - if (httpTaskConfig == null) { - httpTaskConfig = new HttpTaskConfig(); - } - return httpTaskConfig; - } - - /** - * 设置或获取FTP任务相关参数 - */ - public FtpTaskConfig asFtp() { - if (ftpTaskConfig == null) { - ftpTaskConfig = new FtpTaskConfig(); - } - return ftpTaskConfig; - } - /** * 获取任务下载状态 * @@ -134,6 +124,8 @@ public abstract class AbsTaskWrapper return getEntity().getState(); } + public abstract BaseTaskConfig getConfig(); + public boolean isRefreshInfo() { return refreshInfo; } @@ -154,6 +146,7 @@ public abstract class AbsTaskWrapper entity.setState(state); } + @Override public int getRequestType() { return requestType; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java similarity index 80% rename from Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java index 192be8b5..e2b628ff 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ITaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.wrapper; -import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IEntity; /** - * 组合任务实体包裹器,用于加载和任务相关的参数,如:组合任务实体{@link DownloadGroupEntity}、header头部 + * 组合任务实体包裹器,用于加载和任务相关的参数 */ -public interface ITaskWrapper { +public interface ITaskWrapper { int DEFAULT = 0; @@ -79,5 +79,20 @@ public interface ITaskWrapper { */ int U_TCP_PEER = 11; - ENTITY getEntity(); + /** + * 获取任务类型 + * + * @return {@link ITaskWrapper} + */ + int getRequestType(); + + /** + * 获取任务实体 + */ + IEntity getEntity(); + + /** + * 获取任务唯一标志 + */ + String getKey(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/RecordWrapper.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/RecordWrapper.java index 05e77cf8..bbd9c4c1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/RecordWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/RecordWrapper.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.core.wrapper; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.orm.AbsDbWrapper; import com.arialyy.aria.orm.annotation.Many; import com.arialyy.aria.orm.annotation.One; diff --git a/Aria/src/main/java/com/arialyy/aria/exception/AriaIOException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/AriaIOException.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/AriaIOException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/AriaIOException.java diff --git a/Aria/src/main/java/com/arialyy/aria/exception/BaseException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/BaseException.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/BaseException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/BaseException.java diff --git a/aria/src/main/java/com/arialyy/aria/exception/FileException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/FileException.java similarity index 100% rename from aria/src/main/java/com/arialyy/aria/exception/FileException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/FileException.java diff --git a/Aria/src/main/java/com/arialyy/aria/exception/FileNotFoundException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/FileNotFoundException.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/FileNotFoundException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/FileNotFoundException.java diff --git a/Aria/src/main/java/com/arialyy/aria/exception/M3U8Exception.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/M3U8Exception.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/M3U8Exception.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/M3U8Exception.java diff --git a/Aria/src/main/java/com/arialyy/aria/exception/ParamException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/ParamException.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/ParamException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/ParamException.java diff --git a/Aria/src/main/java/com/arialyy/aria/exception/TaskException.java b/PublicComponent/src/main/java/com/arialyy/aria/exception/TaskException.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/exception/TaskException.java rename to PublicComponent/src/main/java/com/arialyy/aria/exception/TaskException.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/AbsDbWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDbWrapper.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/AbsDbWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDbWrapper.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/AbsDelegate.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/ActionPolicy.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java similarity index 84% rename from Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java index fae9bb31..d74f02dc 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -16,16 +16,12 @@ package com.arialyy.aria.orm; import android.text.TextUtils; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; +import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UTaskWrapper; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DatabaseContext.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/DatabaseContext.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DatabaseContext.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DbEntity.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DbEntity.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java index 3408a8af..2fc2dcdd 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -21,7 +21,6 @@ import android.text.TextUtils; import com.arialyy.aria.orm.annotation.Default; import com.arialyy.aria.orm.annotation.Foreign; import com.arialyy.aria.orm.annotation.Primary; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.util.ArrayList; @@ -106,7 +105,7 @@ class DelegateCommon extends AbsDelegate { */ boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { db = checkDb(db); - if (!CheckUtil.checkSqlExpression(expression)) { + if (!CommonUtil.checkSqlExpression(expression)) { return false; } String sql = String.format("SELECT rowid, * FROM %s WHERE %s ", CommonUtil.getClassName(clazz), diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java similarity index 99% rename from Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java index a008628f..6fdd6695 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -23,7 +23,6 @@ import com.arialyy.aria.orm.annotation.Many; import com.arialyy.aria.orm.annotation.One; import com.arialyy.aria.orm.annotation.Wrapper; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.net.URLDecoder; @@ -198,7 +197,7 @@ class DelegateFind extends AbsDelegate { .append(cTableName.concat(".").concat(m.entityColumn())); String sql; if (expression != null && expression.length > 0) { - if (!CheckUtil.checkSqlExpression(expression)) { + if (!CommonUtil.checkSqlExpression(expression)) { return null; } sb.append(" WHERE ").append(expression[0]).append(" "); @@ -320,7 +319,7 @@ class DelegateFind extends AbsDelegate { */ List findData(SQLiteDatabase db, Class clazz, String... expression) { db = checkDb(db); - if (!CheckUtil.checkSqlExpression(expression)) { + if (!CommonUtil.checkSqlExpression(expression)) { return null; } String sql = String.format("SELECT rowid, * FROM %s WHERE %s", CommonUtil.getClassName(clazz), @@ -346,7 +345,7 @@ class DelegateFind extends AbsDelegate { return null; } db = checkDb(db); - if (!CheckUtil.checkSqlExpression(expression)) { + if (!CommonUtil.checkSqlExpression(expression)) { return null; } String sql = String.format("SELECT rowid, * FROM %s WHERE %s LIMIT %s,%s", diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateManager.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/DelegateManager.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateManager.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java index 669e0280..9835b250 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -20,7 +20,6 @@ import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -40,7 +39,7 @@ class DelegateUpdate extends AbsDelegate { synchronized void delData(SQLiteDatabase db, Class clazz, String... expression) { db = checkDb(db); - if (!CheckUtil.checkSqlExpression(expression)) { + if (!CommonUtil.checkSqlExpression(expression)) { return; } diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/SqlUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Default.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Default.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Default.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Foreign.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Ignore.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Many.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Many.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Many.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/NoNull.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/One.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/One.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/One.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Primary.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Primary.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Primary.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Table.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Table.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Table.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Unique.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Unique.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Unique.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Unique.java diff --git a/Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java rename to PublicComponent/src/main/java/com/arialyy/aria/orm/annotation/Wrapper.java diff --git a/Aria/src/main/java/com/arialyy/aria/util/ALog.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ALog.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/util/ALog.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/ALog.java diff --git a/Aria/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/AriaCrashHandler.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/BandwidthLimiter.java b/PublicComponent/src/main/java/com/arialyy/aria/util/BandwidthLimiter.java similarity index 98% rename from Aria/src/main/java/com/arialyy/aria/core/common/BandwidthLimiter.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/BandwidthLimiter.java index b42a58ff..fff1e553 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/BandwidthLimiter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/BandwidthLimiter.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.common; +package com.arialyy.aria.util; /** * 速度限制 diff --git a/Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java b/PublicComponent/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/BufferedRandomAccessFile.java diff --git a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java similarity index 85% rename from Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index ef291be5..345c6560 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -19,15 +19,12 @@ package com.arialyy.aria.util; import android.text.TextUtils; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.exception.ParamException; import java.io.File; -import java.util.Arrays; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Created by Lyy on 2016/9/23. @@ -67,42 +64,6 @@ public class CheckUtil { if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); } - /** - * 检查sql的expression是否合法 - * - * @return false 不合法 - */ - public static boolean checkSqlExpression(String... expression) { - if (expression.length == 0) { - ALog.e(TAG, "sql语句表达式不能为null"); - return false; - } - if (expression.length == 1) { - ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression))); - return false; - } - String where = expression[0]; - if (!where.contains("?")) { - ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression))); - return false; - } - Pattern pattern = Pattern.compile("\\?"); - Matcher matcher = pattern.matcher(where); - int count = 0; - while (matcher.find()) { - count++; - } - if (count < expression.length - 1) { - ALog.e(TAG, String.format("条件语句的?个数不能小于参数个数,参数信息:%s", Arrays.toString(expression))); - return false; - } - if (count > expression.length - 1) { - ALog.e(TAG, String.format("条件语句的?个数不能大于参数个数, 参数信息:%s", Arrays.toString(expression))); - return false; - } - return true; - } - /** * 检查下载实体 */ diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java similarity index 91% rename from Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index 1d0871c3..d2713dc6 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -21,17 +21,17 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Environment; +import android.os.Handler; import android.text.TextUtils; import android.util.Base64; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.command.AbsGroupCmd; -import com.arialyy.aria.core.command.AbsNormalCmd; -import com.arialyy.aria.core.command.GroupCmdFactory; -import com.arialyy.aria.core.command.NormalCmdFactory; -import com.arialyy.aria.core.common.ftp.FtpUrlEntity; -import com.arialyy.aria.core.inf.AbsGroupTaskWrapper; -import com.arialyy.aria.core.inf.AbsTaskWrapper; -import com.arialyy.aria.core.inf.ITask; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.TaskOptionParams; +import com.arialyy.aria.core.inf.IEventHandler; +import com.arialyy.aria.core.inf.ITaskOption; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import dalvik.system.DexFile; import java.io.File; import java.io.FileFilter; @@ -41,6 +41,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -50,6 +51,7 @@ import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; @@ -67,6 +69,43 @@ public class CommonUtil { public static final String SERVER_CHARSET = "ISO-8859-1"; private static long lastClickTime; + + /** + * 检查sql的expression是否合法 + * + * @return false 不合法 + */ + public static boolean checkSqlExpression(String... expression) { + if (expression.length == 0) { + ALog.e(TAG, "sql语句表达式不能为null"); + return false; + } + if (expression.length == 1) { + ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression))); + return false; + } + String where = expression[0]; + if (!where.contains("?")) { + ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression))); + return false; + } + Pattern pattern = Pattern.compile("\\?"); + Matcher matcher = pattern.matcher(where); + int count = 0; + while (matcher.find()) { + count++; + } + if (count < expression.length - 1) { + ALog.e(TAG, String.format("条件语句的?个数不能小于参数个数,参数信息:%s", Arrays.toString(expression))); + return false; + } + if (count > expression.length - 1) { + ALog.e(TAG, String.format("条件语句的?个数不能大于参数个数, 参数信息:%s", Arrays.toString(expression))); + return false; + } + return true; + } + /** * 是否是快速点击,500ms内快速点击无效 * @@ -543,26 +582,6 @@ public class CommonUtil { } } - /** - * 创建任务命令 - * - * @param taskType {@link ITask#DOWNLOAD}、{@link ITask#DOWNLOAD_GROUP}、{@link ITask#UPLOAD} - */ - public static AbsNormalCmd createNormalCmd(T entity, int cmd, - int taskType) { - return NormalCmdFactory.getInstance().createCmd(entity, cmd, taskType); - } - - /** - * 创建任务组命令 - * - * @param childUrl 子任务url - */ - public static AbsGroupCmd createGroupCmd(T entity, int cmd, - String childUrl) { - return GroupCmdFactory.getInstance().createCmd(entity, cmd, childUrl); - } - /** * 创建隐性的Intent */ @@ -770,8 +789,8 @@ public class CommonUtil { * @param fileName 文件名 */ public static String getFileConfigPath(boolean isDownload, String fileName) { - return AriaManager.getInstance().getAPP().getFilesDir().getPath() + (isDownload - ? AriaManager.DOWNLOAD_TEMP_DIR - : AriaManager.UPLOAD_TEMP_DIR) + fileName + ".properties"; + return AriaConfig.getInstance().getAPP().getFilesDir().getPath() + (isDownload + ? AriaConfig.DOWNLOAD_TEMP_DIR + : AriaConfig.UPLOAD_TEMP_DIR) + fileName + ".properties"; } } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java new file mode 100644 index 00000000..37d93aee --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.util; + +import android.os.Handler; +import com.arialyy.aria.core.TaskOptionParams; +import com.arialyy.aria.core.inf.IEventHandler; +import com.arialyy.aria.core.inf.ITaskOption; +import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.listener.BaseListener; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import java.lang.ref.SoftReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * 组件工具,用于跨组件创建对应的工具类 + * + * @Author lyy + * @Date 2019-09-23 + */ +public class ComponentUtil { + public static final int COMPONENT_TYPE_HTTP = 1; + public static final int COMPONENT_TYPE_FTP = 2; + public static final int COMPONENT_TYPE_M3U8 = 3; + + private static volatile ComponentUtil INSTANCE = null; + private String TAG = CommonUtil.getClassName(getClass()); + + private ComponentUtil() { + + } + + public static ComponentUtil getInstance() { + if (INSTANCE == null) { + synchronized (ComponentUtil.class) { + if (INSTANCE == null) { + INSTANCE = new ComponentUtil(); + } + } + } + + return INSTANCE; + } + + /** + * 检查组件是否存在,不存在抛出异常退出 + * + * @param componentType 组件类型{@link #COMPONENT_TYPE_FTP}, {@link #COMPONENT_TYPE_M3U8}, {@link + * #COMPONENT_TYPE_HTTP} + * @return true 组件存在 + */ + public boolean checkComponentExist(int componentType) { + String errorStr = "", className = null; + switch (componentType) { + case COMPONENT_TYPE_M3U8: + className = "com.arialyy.aria.m3u8.M3U8TaskOption"; + errorStr = "m3u8插件不存在,请添加m3u8插件"; + break; + case COMPONENT_TYPE_FTP: + className = "com.arialyy.aria.ftp.FtpTaskOption"; + errorStr = "ftp插件不存在,请添加ftp插件"; + break; + case COMPONENT_TYPE_HTTP: + className = "com.arialyy.aria.http.HttpTaskOption"; + errorStr = "http插件不存在,请添加http插件"; + break; + } + + try { + getClass().getClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new IllegalArgumentException(errorStr); + } + return true; + } + + /** + * 创建下载工具 + * + * @return 返回下载工具,创建失败返回null + */ + public T buildUtil(ITaskWrapper wrapper, IEventListener listener) { + int requestType = wrapper.getRequestType(); + String className = null; + switch (requestType) { + case ITaskWrapper.M3U8_LIVE: + className = "com.arialyy.aria.m3u8.live.M3U8LiveUtil"; + break; + case ITaskWrapper.M3U8_VOD: + className = "com.arialyy.aria.m3u8.vod.M3U8VodUtil"; + break; + case ITaskWrapper.D_FTP: + className = "com.arialyy.aria.ftp.download.FtpDLoaderUtil"; + break; + case ITaskWrapper.D_HTTP: + className = "com.arialyy.aria.http.download.HttpDLoaderUtil"; + break; + case ITaskWrapper.U_FTP: + className = "com.arialyy.aria.ftp.upload.FtpULoaderUtil"; + break; + case ITaskWrapper.U_HTTP: + className = "com.arialyy.aria.http.upload.HttpULoaderUtil"; + break; + case ITaskWrapper.D_FTP_DIR: + className = "com.arialyy.aria.http.download.DGroupLoaderUtil"; + break; + case ITaskWrapper.DG_HTTP: + className = "com.arialyy.aria.ftp.download.FtpDirDLoaderUtil"; + break; + } + if (className == null) { + return null; + } + T util = null; + try { + Class clazz = (Class) getClass().getClassLoader().loadClass(className); + Class[] paramTypes = { AbsTaskWrapper.class, IEventListener.class }; + Object[] params = { wrapper, listener }; + Constructor con = clazz.getConstructor(paramTypes); + util = con.newInstance(params); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return util; + } + + /** + * 创建任务事件监听 + * + * @param wrapperType 任务类型{@link ITaskWrapper} + * @return 返回事件监听,如果创建失败返回null + */ + public T buildListener(int wrapperType, ITask task, + Handler outHandler) { + String className = null, errorStr = "请添加FTP插件"; + switch (wrapperType) { + case ITaskWrapper.M3U8_LIVE: + case ITaskWrapper.M3U8_VOD: + className = "com.arialyy.aria.m3u8.M3U8Listener"; + errorStr = "请添加m3u8插件"; + break; + case ITaskWrapper.D_FTP: + case ITaskWrapper.D_HTTP: + className = "com.arialyy.aria.core.listener.BaseDListener"; + break; + case ITaskWrapper.U_FTP: + case ITaskWrapper.U_HTTP: + className = "com.arialyy.aria.core.listener.BaseUListener"; + break; + case ITaskWrapper.DG_HTTP: + case ITaskWrapper.D_FTP_DIR: + className = "com.arialyy.aria.core.listener.DownloadGroupListener"; + break; + } + if (className == null) { + return null; + } + T listener = null; + try { + Class clazz = (Class) getClass().getClassLoader().loadClass(className); + Class[] paramTypes = { AbsTask.class, Handler.class }; + Object[] params = { task, outHandler }; + Constructor con = clazz.getConstructor(paramTypes); + listener = con.newInstance(params); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(errorStr); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return listener; + } + + /** + * 构建TaskOption信息 + * + * @param clazz 实现{@link ITaskOption}接口的配置信息类 + * @param params 任务配置信息参数 + * @return 构建失败,返回null + */ + public T buildTaskOption(Class clazz, TaskOptionParams params) { + Field[] fields = CommonUtil.getFields(clazz); + T taskOption = null; + try { + taskOption = clazz.newInstance(); + for (Field field : fields) { + Class type = field.getType(); + String key = field.getName(); + if (type != SoftReference.class) { + Object obj = params.getParams().get(key); + if (obj == null) { + continue; + } + field.set(taskOption, obj); + } else { + IEventHandler handler = params.getHandler().get(key); + if (handler == null) { + continue; + } + field.set(taskOption, new SoftReference<>(handler)); + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } + return taskOption; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java index 95cb5789..d9382dd4 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/DbDataHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java @@ -15,8 +15,8 @@ */ package com.arialyy.aria.util; -import com.arialyy.aria.core.common.RecordWrapper; -import com.arialyy.aria.core.common.TaskRecord; +import com.arialyy.aria.core.wrapper.RecordWrapper; +import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.download.DGEntityWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; diff --git a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java index 2fbee6a1..a89b933e 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/ErrorHelp.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java @@ -16,7 +16,7 @@ package com.arialyy.aria.util; import android.annotation.SuppressLint; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -47,7 +47,7 @@ public class ErrorHelp { */ private static String getLogPath() { String path = String.format("%slog/AriaCrash_%s.log", - CommonUtil.getAppPath(AriaManager.getInstance().getAPP()), + CommonUtil.getAppPath(AriaConfig.getInstance().getAPP()), getData("yyyy-MM-dd_HH_mm_ss")); File log = new File(path); diff --git a/Aria/src/main/java/com/arialyy/aria/util/FileUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java similarity index 99% rename from Aria/src/main/java/com/arialyy/aria/util/FileUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java index 937caba2..424c4679 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/FileUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java @@ -25,7 +25,7 @@ import android.os.StatFs; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -364,7 +364,7 @@ public class FileUtil { * @return {@code false} 内存空间不足,{@code true}内存空间足够 */ public static boolean checkSDMemorySpace(String filePath, long fileSize) { - List dirs = FileUtil.getSDPathList(AriaManager.getInstance().getAPP()); + List dirs = FileUtil.getSDPathList(AriaConfig.getInstance().getAPP()); if (dirs == null || dirs.isEmpty()) { return true; } @@ -546,7 +546,7 @@ public class FileUtil { /** * Raturns all available SD-Cards in the system (include emulated) Warning: Hack! Based on Android * source code of version 4.3 - * (API 18) Because there is no standard way to get it. TODO: Test on future Android versions + * (API 18) Because there is no standard way to get it. * 4.2+ * * @return paths to all available SD-Cards in the system (include emulated) diff --git a/Aria/src/main/java/com/arialyy/aria/util/NetUtils.java b/PublicComponent/src/main/java/com/arialyy/aria/util/NetUtils.java similarity index 96% rename from Aria/src/main/java/com/arialyy/aria/util/NetUtils.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/NetUtils.java index 2200f03a..dac9de4d 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/NetUtils.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/NetUtils.java @@ -23,8 +23,7 @@ import android.net.NetworkInfo; import android.os.Build; import android.telephony.TelephonyManager; import android.text.TextUtils; -import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; /** * 跟网络相关的工具类 @@ -58,11 +57,11 @@ public class NetUtils { * @return {@code true} 网络已连接、{@code false}网络未连接 */ public static boolean isConnected(Context context) { - if (!Aria.get(context).getAppConfig().isNetCheck()) { + if (!AriaConfig.getInstance().getAConfig().isNetCheck()) { return true; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return AriaManager.getInstance().isConnectedNet(); + return AriaConfig.getInstance().isConnectedNet(); } ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java similarity index 89% rename from Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 5f83d08c..ce8a9b58 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -16,15 +16,15 @@ package com.arialyy.aria.util; import android.text.TextUtils; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.common.RecordWrapper; -import com.arialyy.aria.core.common.TaskRecord; -import com.arialyy.aria.core.common.ThreadRecord; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.wrapper.RecordWrapper; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsNormalEntity; +import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; import java.io.File; @@ -78,7 +78,7 @@ public class RecordUtil { if (record.taskRecord.isBlock) { for (int i = 0, len = record.taskRecord.threadNum; i < len; i++) { File partFile = - new File(String.format(RecordHandler.SUB_PATH, record.taskRecord.filePath, i)); + new File(String.format(IRecordHandler.SUB_PATH, record.taskRecord.filePath, i)); if (partFile.exists()) { partFile.delete(); } @@ -124,10 +124,10 @@ public class RecordUtil { String filePath; int type; if (entity instanceof DownloadEntity) { - type = RecordHandler.TYPE_DOWNLOAD; + type = IRecordHandler.TYPE_DOWNLOAD; filePath = ((DownloadEntity) entity).getDownloadPath(); } else if (entity instanceof UploadEntity) { - type = RecordHandler.TYPE_UPLOAD; + type = IRecordHandler.TYPE_UPLOAD; filePath = ((UploadEntity) entity).getFilePath(); } else { ALog.w(TAG, "删除记录失败,未知类型"); @@ -171,7 +171,7 @@ public class RecordUtil { * @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件; * {@code false} 如果是下载任务,并且任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。 * 如果是上传任务,无论任务是否完成,都只删除记录 - * @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD} + * @param type {@link IRecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link IRecordHandler#TYPE_UPLOAD} * 上传任务的记录 * @param removeEntity {@code true} 删除任务实体, */ @@ -183,12 +183,12 @@ public class RecordUtil { if (!filePath.startsWith("/")) { throw new IllegalArgumentException(String.format("文件路径错误,filePath:%s", filePath)); } - if (type != RecordHandler.TYPE_DOWNLOAD && type != RecordHandler.TYPE_UPLOAD) { + if (type != IRecordHandler.TYPE_DOWNLOAD && type != IRecordHandler.TYPE_UPLOAD) { throw new IllegalArgumentException("任务记录类型错误"); } AbsEntity entity; - if (type == RecordHandler.TYPE_DOWNLOAD) { + if (type == IRecordHandler.TYPE_DOWNLOAD) { entity = DbEntity.findFirst(DownloadEntity.class, "downloadPath=?", filePath); } else { entity = DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath); @@ -245,11 +245,11 @@ public class RecordUtil { /** * 删除实体 * - * @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD} + * @param type {@link IRecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link IRecordHandler#TYPE_UPLOAD} * 上传任务的记录 */ private static void removeEntity(int type, String filePath) { - if (type == RecordHandler.TYPE_DOWNLOAD) { + if (type == IRecordHandler.TYPE_DOWNLOAD) { DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath); } else { DbEntity.deleteData(UploadEntity.class, "filePath=?", filePath); @@ -277,7 +277,7 @@ public class RecordUtil { */ private static void removeBlockFile(TaskRecord record) { for (int i = 0, len = record.threadNum; i < len; i++) { - File partFile = new File(String.format(RecordHandler.SUB_PATH, record.filePath, i)); + File partFile = new File(String.format(IRecordHandler.SUB_PATH, record.filePath, i)); if (partFile.exists()) { partFile.delete(); } @@ -317,7 +317,7 @@ public class RecordUtil { * 删除任务记录,默认删除文件,删除任务实体 * * @param filePath 文件路径 - * @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD} + * @param type {@link IRecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link IRecordHandler#TYPE_UPLOAD} * 上传任务的记录 */ public static void delTaskRecord(String filePath, int type) { @@ -355,9 +355,9 @@ public class RecordUtil { if (record.threadRecords != null && !record.threadRecords.isEmpty()) { for (ThreadRecord tr : record.threadRecords) { tr.taskKey = newPath; - File blockFile = new File(String.format(RecordHandler.SUB_PATH, oldPath, tr.threadId)); + File blockFile = new File(String.format(IRecordHandler.SUB_PATH, oldPath, tr.threadId)); if (blockFile.exists()) { - blockFile.renameTo(new File(String.format(RecordHandler.SUB_PATH, newPath, tr.threadId))); + blockFile.renameTo(new File(String.format(IRecordHandler.SUB_PATH, newPath, tr.threadId))); } } DbEntity.updateManyData(record.threadRecords); @@ -371,7 +371,7 @@ public class RecordUtil { * @return {@code true} 分块文件存在 */ public static boolean blockTaskExists(String filePath) { - return new File(String.format(RecordHandler.SUB_PATH, filePath, 0)).exists(); + return new File(String.format(IRecordHandler.SUB_PATH, filePath, 0)).exists(); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/util/Regular.java b/PublicComponent/src/main/java/com/arialyy/aria/util/Regular.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/util/Regular.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/Regular.java diff --git a/Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java similarity index 97% rename from Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java index a44f36ea..78395132 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java @@ -16,8 +16,8 @@ package com.arialyy.aria.util; import android.text.TextUtils; -import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.ProtocolType; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.ProtocolType; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -58,7 +58,7 @@ public class SSLContextUtil { */ public static SSLContext getSSLContextFromAssets(String caAlias, String caPath, @ProtocolType String protocol) { - if (TextUtils.isEmpty(caAlias) || TextUtils.isEmpty(caPath)){ + if (TextUtils.isEmpty(caAlias) || TextUtils.isEmpty(caPath)) { return null; } try { @@ -67,7 +67,7 @@ public class SSLContextUtil { if (sslContext != null) { return sslContext; } - InputStream caInput = AriaManager.getInstance().getAPP().getAssets().open(caPath); + InputStream caInput = AriaConfig.getInstance().getAPP().getAssets().open(caPath); Certificate ca = loadCert(caInput); return createContext(caAlias, ca, protocol, cacheKey); } catch (IOException | CertificateException e) { diff --git a/Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/util/WeakHandler.java rename to PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java diff --git a/PublicComponent/src/main/res/values/strings.xml b/PublicComponent/src/main/res/values/strings.xml new file mode 100644 index 00000000..568a9247 --- /dev/null +++ b/PublicComponent/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + PublicComponent + diff --git a/app/build.gradle b/app/build.gradle index 76cc2bad..7d4bca30 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,7 +60,7 @@ dependencies { implementation 'com.google.android.material:material:1.0.0' implementation "org.jetbrains.kotlin:kotlin-stdlib:${rootProject.ext.kotlin_version}" - api project(':Aria') + implementation project(':Aria') kapt project(':AriaCompiler') // api 'com.arialyy.aria:aria-core:3.6.4' // kapt 'com.arialyy.aria:aria-compiler:3.6.4' diff --git a/app/src/main/assets/help_code/FtpUpload.java b/app/src/main/assets/help_code/FtpUpload.java index 48ad3c99..1a977623 100644 --- a/app/src/main/assets/help_code/FtpUpload.java +++ b/app/src/main/assets/help_code/FtpUpload.java @@ -23,7 +23,7 @@ import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.ftp.FtpInterceptHandler; import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.frame.util.FileUtil; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; diff --git a/app/src/main/assets/help_code/KotlinHttpDownload.kt b/app/src/main/assets/help_code/KotlinHttpDownload.kt index 7f3033b9..a1dfe1cb 100644 --- a/app/src/main/assets/help_code/KotlinHttpDownload.kt +++ b/app/src/main/assets/help_code/KotlinHttpDownload.kt @@ -22,8 +22,8 @@ import android.widget.Toast import com.arialyy.annotations.Download import com.arialyy.aria.core.Aria import com.arialyy.aria.core.download.target.HttpNormalTarget -import com.arialyy.aria.core.download.DownloadTask -import com.arialyy.aria.core.inf.IEntity +import com.arialyy.aria.core.task.DownloadTask +import com.arialyy.aria.inf.IEntity import com.arialyy.simple.R import com.arialyy.simple.base.BaseActivity import com.arialyy.simple.databinding.ActivitySingleBinding @@ -59,7 +59,7 @@ class KotlinDownloadActivity : BaseActivity() { target = Aria.download(this) .load(DOWNLOAD_URL) binding.progress = target.percent - if (target.taskState == IEntity.STATE_STOP) { + if (target.taskState == com.arialyy.aria.inf.IEntity.STATE_STOP) { mStart.text = "恢复" } else if (target.isRunning) { mStart.text = "停止" @@ -71,7 +71,7 @@ class KotlinDownloadActivity : BaseActivity() { * 注解方法不能添加internal修饰符,否则会出现e: [kapt] An exception occurred: java.lang.IllegalArgumentException: peerIndex 1 for '$a' not in range (received 0 arguments)错误 */ @Download.onTaskRunning - fun running(task: DownloadTask) { + fun running(task: com.arialyy.aria.core.task.DownloadTask) { Log.d(TAG, task.percent.toString()) val len = task.fileSize if (len == 0L) { @@ -83,40 +83,40 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onWait - fun onWait(task: DownloadTask) { + fun onWait(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { Log.d(TAG, "wait ==> " + task.downloadEntity.fileName) } } @Download.onPre - fun onPre(task: DownloadTask) { + fun onPre(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { mStart.text = "停止" } } @Download.onTaskStart - fun taskStart(task: DownloadTask) { + fun taskStart(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { binding.fileSize = task.convertFileSize } } @Download.onTaskComplete - fun complete(task: DownloadTask) { + fun complete(task: com.arialyy.aria.core.task.DownloadTask) { Log.d(TAG, "完成") } @Download.onTaskResume - fun taskResume(task: DownloadTask) { + fun taskResume(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { mStart.text = "停止" } } @Download.onTaskStop - fun taskStop(task: DownloadTask) { + fun taskStop(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { mStart.text = "恢复" binding.speed = "" @@ -124,7 +124,7 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onTaskCancel - fun taskCancel(task: DownloadTask) { + fun taskCancel(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { binding.progress = 0 Toast.makeText(this@KotlinDownloadActivity, "取消下载", Toast.LENGTH_SHORT) @@ -136,7 +136,7 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onTaskFail - fun taskFail(task: DownloadTask) { + fun taskFail(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == DOWNLOAD_URL) { Toast.makeText(this@KotlinDownloadActivity, "下载失败", Toast.LENGTH_SHORT) .show() diff --git a/app/src/main/java/com/arialyy/simple/DbTestActivity.java b/app/src/main/java/com/arialyy/simple/DbTestActivity.java index b1dbe3d4..feb15785 100644 --- a/app/src/main/java/com/arialyy/simple/DbTestActivity.java +++ b/app/src/main/java/com/arialyy/simple/DbTestActivity.java @@ -1,12 +1,11 @@ package com.arialyy.simple; import android.os.Bundle; - import android.util.Log; import android.view.View; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.orm.DbEntity; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityDbTestBinding; @@ -38,7 +37,7 @@ public class DbTestActivity extends BaseActivity { } } - private void searchAll(){ + private void searchAll() { long startT = System.currentTimeMillis(); //List data = DownloadEntity.findRelationData(DownloadEntity.class); diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadDialog.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadDialog.java index d4075d0e..40899abf 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadDialog.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadDialog.java @@ -24,7 +24,7 @@ import android.widget.TextView; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.core.AbsDialog; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadDialogFragment.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadDialogFragment.java index b6755c18..4a1e5895 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadDialogFragment.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadDialogFragment.java @@ -8,8 +8,8 @@ import android.widget.Toast; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseDialog; diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadPopupWindow.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadPopupWindow.java index 95e5856d..4c7e2bd6 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadPopupWindow.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadPopupWindow.java @@ -26,8 +26,8 @@ import android.widget.TextView; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.core.AbsPopupWindow; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 1c9ff07a..94d3a04e 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -23,11 +23,11 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.common.ProtocolType; +import com.arialyy.aria.core.ProtocolType; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; diff --git a/app/src/main/java/com/arialyy/simple/core/download/HighestPriorityActivity.java b/app/src/main/java/com/arialyy/simple/core/download/HighestPriorityActivity.java index dec8a60f..a20803b2 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HighestPriorityActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HighestPriorityActivity.java @@ -25,10 +25,10 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.AbsEntity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index 609ff721..dd449cb9 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -33,7 +33,7 @@ public class HttpDownloadModule extends BaseViewModule { private final String HTTP_PATH_KEY = "HTTP_PATH_KEY"; private final String defUrl = - "http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk1"; + "http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk"; //"http://9.9.9.205:5000/download/Cyberduck-6.9.4.30164.zip"; //"http://202.98.201.103:7000/vrs/TPK/ZTC440402001Z.tpk"; private final String defFilePath = diff --git a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt index 1550420c..8cf91a69 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt +++ b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt @@ -30,9 +30,8 @@ import androidx.lifecycle.ViewModelProviders import com.arialyy.annotations.Download import com.arialyy.aria.core.Aria import com.arialyy.aria.core.download.DownloadEntity -import com.arialyy.aria.core.download.DownloadTask import com.arialyy.aria.core.inf.IEntity -import com.arialyy.aria.core.scheduler.ISchedulers +import com.arialyy.aria.core.listener.ISchedulers import com.arialyy.aria.util.ALog import com.arialyy.aria.util.CommonUtil import com.arialyy.frame.util.show.T @@ -59,10 +58,22 @@ class KotlinDownloadActivity : BaseActivity() { intent: Intent ) { if (intent.action == ISchedulers.ARIA_TASK_INFO_ACTION) { - ALog.d(TAG, "state = " + intent.getIntExtra(ISchedulers.TASK_STATE, -1)) - ALog.d(TAG, "type = " + intent.getIntExtra(ISchedulers.TASK_TYPE, -1)) - ALog.d(TAG, "speed = " + intent.getLongExtra(ISchedulers.TASK_SPEED, -1)) - ALog.d(TAG, "percent = " + intent.getIntExtra(ISchedulers.TASK_PERCENT, -1)) + ALog.d( + TAG, + "state = " + intent.getIntExtra(ISchedulers.TASK_STATE, -1) + ) + ALog.d( + TAG, "type = " + intent.getIntExtra(ISchedulers.TASK_TYPE, -1) + ) + ALog.d( + TAG, + "speed = " + intent.getLongExtra(ISchedulers.TASK_SPEED, -1) + ) + ALog.d( + TAG, "percent = " + intent.getIntExtra( + ISchedulers.TASK_PERCENT, -1 + ) + ) ALog.d( TAG, "entity = " + intent.getParcelableExtra( ISchedulers.TASK_ENTITY @@ -167,28 +178,28 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onWait - fun onWait(task: DownloadTask) { + fun onWait(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { Log.d(TAG, "wait ==> " + task.downloadEntity.fileName) } } @Download.onPre - fun onPre(task: DownloadTask) { + fun onPre(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.stateStr = getString(R.string.stop) } } @Download.onTaskStart - fun taskStart(task: DownloadTask) { + fun taskStart(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.fileSize = task.convertFileSize } } @Download.onTaskRunning - fun running(task: DownloadTask) { + fun running(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { //Log.d(TAG, task.getKey()); val len = task.fileSize @@ -202,14 +213,14 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onTaskResume - fun taskResume(task: DownloadTask) { + fun taskResume(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.stateStr = getString(R.string.stop) } } @Download.onTaskStop - fun taskStop(task: DownloadTask) { + fun taskStop(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.stateStr = getString(R.string.resume) binding.speed = "" @@ -217,7 +228,7 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onTaskCancel - fun taskCancel(task: DownloadTask) { + fun taskCancel(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.progress = 0 binding.stateStr = getString(R.string.start) @@ -231,7 +242,7 @@ class KotlinDownloadActivity : BaseActivity() { */ @Download.onTaskFail fun taskFail( - task: DownloadTask, + task: com.arialyy.aria.core.task.DownloadTask, e: Exception ) { if (task.key == mUrl) { @@ -242,7 +253,7 @@ class KotlinDownloadActivity : BaseActivity() { } @Download.onTaskComplete - fun taskComplete(task: DownloadTask) { + fun taskComplete(task: com.arialyy.aria.core.task.DownloadTask) { if (task.key == mUrl) { binding.progress = 100 diff --git a/app/src/main/java/com/arialyy/simple/core/download/SimpleNotification.java b/app/src/main/java/com/arialyy/simple/core/download/SimpleNotification.java index f3ac70df..ab6e6043 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SimpleNotification.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SimpleNotification.java @@ -22,7 +22,7 @@ //import android.support.v4.app.NotificationCompat; //import com.arialyy.annotations.Download; //import com.arialyy.aria.core.Aria; -//import com.arialyy.aria.core.download.DownloadTask; +//import com.arialyy.aria.core.task.DownloadTask; //import com.arialyy.simple.R; // ///** diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 151d7fa2..88ec8680 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -31,12 +31,12 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; -import com.arialyy.aria.core.scheduler.ISchedulers; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.T; diff --git a/app/src/main/java/com/arialyy/simple/core/download/fragment/DownloadFragment.java b/app/src/main/java/com/arialyy/simple/core/download/fragment/DownloadFragment.java index 994d0295..f65cf865 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/fragment/DownloadFragment.java +++ b/app/src/main/java/com/arialyy/simple/core/download/fragment/DownloadFragment.java @@ -23,8 +23,8 @@ import android.widget.Button; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.core.AbsFragment; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/ChildHandleDialog.java b/app/src/main/java/com/arialyy/simple/core/download/group/ChildHandleDialog.java index c8c2c3c0..944df089 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/ChildHandleDialog.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/ChildHandleDialog.java @@ -27,7 +27,7 @@ import android.widget.TextView; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseDialog; diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index db13ddc4..13256730 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -21,11 +21,11 @@ import android.util.Log; import android.view.View; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.DownloadGroupTask; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IHttpFileLenAdapter; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.util.ALog; import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index 2eac991a..755e4b90 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -22,8 +22,8 @@ import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.download.DownloadGroupTask; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java index 1067b633..69fcb184 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java @@ -29,8 +29,8 @@ import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.download.m3u8.ILiveTsUrlConverter; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.T; @@ -200,7 +200,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity Toast.LENGTH_SHORT).show(); getBinding().setStateStr(getString(R.string.re_start)); getBinding().setSpeed(""); - ALog.d(TAG, "md5: " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + ALog.d(TAG, "md5: " + CommonUtil.getFileMD5(new File(task.getFilePath()))); } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index 53d746e2..60086a2a 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -33,11 +33,11 @@ import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.controller.BuilderController; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.download.m3u8.ITsMergeHandler; -import com.arialyy.aria.core.download.m3u8.IVodTsUrlConverter; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; +import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.T; @@ -259,7 +259,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { Toast.LENGTH_SHORT).show(); getBinding().setStateStr(getString(R.string.re_start)); getBinding().setSpeed(""); - ALog.d(TAG, "md5: " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + ALog.d(TAG, "md5: " + CommonUtil.getFileMD5(new File(task.getFilePath()))); } } @@ -350,6 +350,16 @@ public class M3U8VodDLoadActivity extends BaseActivity { .generateIndexFile() .controller(ControllerType.CREATE_CONTROLLER) .create(); + + Aria.download(M3U8VodDLoadActivity.this) + .load(mUrl) + .useServerFileName(true) + .setFilePath(mFilePath, true) + .asM3U8().setTsUrlConvert(new IVodTsUrlConverter() { + @Override public List convert(String m3u8Url, List tsUrls) { + return null; + } + }); } private Class c = BuilderController.class; diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java index d5d001c3..501da945 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java @@ -13,7 +13,7 @@ import android.widget.SeekBar; import androidx.annotation.RequiresApi; import androidx.lifecycle.ViewModelProviders; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.m3u8.M3U8Entity; +import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.util.ALog; import com.arialyy.frame.base.BaseFragment; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java index 902b90cf..f59fb5d6 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java @@ -24,11 +24,11 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.AbsEntity; -import com.arialyy.aria.core.inf.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.CommonUtil; import com.arialyy.simple.R; import com.arialyy.simple.base.adapter.AbsHolder; diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java index 4b46ddb7..503a674e 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java @@ -25,9 +25,9 @@ import androidx.recyclerview.widget.RecyclerView; import com.arialyy.annotations.Download; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; @@ -54,7 +54,7 @@ public class MultiDownloadActivity extends BaseActivity temps = Aria.download(this).getTotalTaskList(); if (temps != null && !temps.isEmpty()) { - for (AbsEntity temp : temps){ + for (AbsEntity temp : temps) { ALog.d(TAG, "state = " + temp.getState()); } mData.addAll(temps); diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiTaskActivity.java index 1e390ad7..c3250673 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiTaskActivity.java @@ -26,8 +26,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.arialyy.annotations.Download; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.download.DownloadGroupTask; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.frame.util.FileUtil; import com.arialyy.simple.R; @@ -105,7 +105,7 @@ public class MultiTaskActivity extends BaseActivity { } @Download.onTaskComplete void taskComplete(DownloadTask task) { - Log.d(TAG, FileUtil.getFileMD5(new File(task.getDownloadPath()))); + Log.d(TAG, FileUtil.getFileMD5(new File(task.getFilePath()))); } //############################### 任务组 ############################## diff --git a/app/src/main/java/com/arialyy/simple/core/download/service/DownloadService.java b/app/src/main/java/com/arialyy/simple/core/download/service/DownloadService.java index 29a54e98..8d7745a4 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/service/DownloadService.java +++ b/app/src/main/java/com/arialyy/simple/core/download/service/DownloadService.java @@ -22,7 +22,7 @@ import android.os.IBinder; import androidx.annotation.Nullable; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.frame.util.show.T; /** diff --git a/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java b/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java index 073f3b4a..1333f538 100644 --- a/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/test/AnyRunActivity.java @@ -1,7 +1,6 @@ package com.arialyy.simple.core.test; import android.os.Bundle; -import android.util.Log; import android.view.View; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.download.DTaskWrapper; diff --git a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java index a6535705..a3631530 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java @@ -25,12 +25,12 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.common.controller.ControllerType; -import com.arialyy.aria.core.common.ftp.FtpInterceptHandler; -import com.arialyy.aria.core.common.ftp.IFtpUploadInterceptor; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.FileUtil; diff --git a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java index 992d83f5..49eb0a00 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java @@ -23,7 +23,7 @@ import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.upload.UploadTask; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.frame.util.FileUtil; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; diff --git a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java index 6b87c9b6..9bd6459f 100644 --- a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java +++ b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java @@ -22,7 +22,7 @@ import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.L; import com.arialyy.simple.util.AppUtil; @@ -77,7 +77,7 @@ public class AnyRunnModule { @Download.onTaskComplete void taskComplete(DownloadTask task) { L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); - L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); + L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getFilePath()))); } public void start(String url) { diff --git a/app/src/main/java/com/arialyy/simple/util/AppUtil.java b/app/src/main/java/com/arialyy/simple/util/AppUtil.java index e22e75c7..0a0582b0 100644 --- a/app/src/main/java/com/arialyy/simple/util/AppUtil.java +++ b/app/src/main/java/com/arialyy/simple/util/AppUtil.java @@ -24,7 +24,7 @@ import android.net.Uri; import android.os.Build; import android.text.TextUtils; import androidx.core.content.FileProvider; -import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.FileUtil; diff --git a/build.gradle b/build.gradle index 40ad5c26..2a99294f 100644 --- a/build.gradle +++ b/build.gradle @@ -41,9 +41,11 @@ task clean(type: Delete) { } ext { + versionCode = 370 + versionName = '3.7' userOrg = 'arialyy' groupId = 'com.arialyy.aria' - publishVersion = '3.6.6' + publishVersion = versionName // publishVersion = '1.0.4' //FTP插件 repoName='maven' desc = 'android 下载框架' diff --git a/settings.gradle b/settings.gradle index b6d6bae2..04e09e40 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ -include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug', ':AppFrame', ':AriaFtpComponent' +include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug', ':AppFrame', ':HttpComponent', ':M3U8Component', + ':FtpComponent', ':PublicComponent' From 75ff995d811c6091aaa0406dbb74a052af28828e Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 30 Sep 2019 18:19:03 +0800 Subject: [PATCH 003/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E7=9A=84=E5=87=A0=E4=B8=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/CheckDEntityUtil.java | 7 ++-- .../aria/core/download/CheckDGEntityUtil.java | 3 +- .../aria/core/download/m3u8/M3U8Delegate.java | 6 +-- .../core/download/m3u8/M3U8LiveDelegate.java | 2 +- .../download/target/HttpBuilderTarget.java | 2 +- .../ftp/download/FtpDThreadTaskAdapter.java | 14 +++---- .../ftp/upload/FtpUThreadTaskAdapter.java | 6 +-- .../aria/http/download/DGroupLoaderUtil.java | 6 ++- .../http/download/HttpDThreadTaskAdapter.java | 14 +++---- .../http/upload/HttpUThreadTaskAdapter.java | 2 +- .../com/arialyy/aria/m3u8/M3U8Listener.java | 3 +- .../com/arialyy/aria/m3u8/M3U8TaskOption.java | 6 +-- .../aria/m3u8/M3U8ThreadTaskAdapter.java | 22 +++++----- .../arialyy/aria/m3u8/live/M3U8LiveUtil.java | 25 +++++++----- .../arialyy/aria/m3u8/vod/M3U8VodUtil.java | 32 ++++++++------- .../aria/core/common/RecordHandler.java | 3 ++ .../arialyy/aria/core/group/AbsGroupUtil.java | 10 +++-- .../core/listener/DownloadGroupListener.java | 7 ++-- .../aria/core/task/AbsThreadTaskAdapter.java | 10 ++--- .../arialyy/aria/core/task/ThreadTask.java | 40 ++++++++----------- .../com/arialyy/aria/util/ComponentUtil.java | 13 +++--- app/build.gradle | 1 + .../download/m3u8/M3U8VodDLoadActivity.java | 18 +-------- app/src/main/res/layout/activity_m3u8_vod.xml | 1 + .../main/res/layout/fragment_video_player.xml | 1 + 25 files changed, 122 insertions(+), 132 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 7bc96639..141d4f31 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -64,7 +64,8 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private void handleM3U8() { File file = new File(mWrapper.getTempFilePath()); - int bandWidth = (int) mWrapper.getM3U8Params().getParam(IOptionConstant.bandWidth); + Object bw = mWrapper.getM3U8Params().getParam(IOptionConstant.bandWidth); + int bandWidth = bw == null ? 0 : (int) bw; // 缓存文件夹格式:问文件夹/.文件名_码率 String cacheDir = String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); @@ -153,8 +154,8 @@ public class CheckDEntityUtil implements ICheckEntityUtil { mEntity.setFileName(newFile.getName()); // 如过使用Content-Disposition中的文件名,将不会执行重命名工作 - if ((boolean) mWrapper.getOptionParams().getParam(IOptionConstant.useServerFileName) - || mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { + Object usf = mWrapper.getOptionParams().getParam(IOptionConstant.useServerFileName); + if ((usf != null && (boolean) usf) || mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { return true; } if (!TextUtils.isEmpty(mEntity.getFilePath())) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 8a1b222a..3fd4f450 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -134,8 +134,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { return false; } - if (mWrapper.getOptionParams().getParam(IOptionConstant.requestEnum) - == RequestEnum.POST) { + if (mWrapper.getOptionParams().getParam(IOptionConstant.requestEnum) == RequestEnum.POST) { for (DTaskWrapper subWrapper : mWrapper.getSubTaskWrapper()) { subWrapper.getOptionParams().setParams(IOptionConstant.requestEnum, RequestEnum.POST); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java index f498d3cb..348cc805 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java @@ -67,7 +67,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setMergeHandler(ITsMergeHandler handler) { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.mergeHandler, handler); + mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.mergeHandler, handler); return this; } @@ -79,7 +79,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setTsUrlConvert(IVodTsUrlConverter converter) { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.vodUrlConverter, converter); + mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.vodUrlConverter, converter); return this; } @@ -102,7 +102,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setBandWidthUrlConverter(IBandWidthUrlConverter converter) { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.bandWidthUrlConverter, converter); + mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.bandWidthUrlConverter, converter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java index 353ce357..70acc618 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java @@ -44,7 +44,7 @@ public class M3U8LiveDelegate extends BaseDelegate setLiveTsUrlConvert(ILiveTsUrlConverter converter) { ((DTaskWrapper) getTaskWrapper()).getM3U8Params() - .setParams(IOptionConstant.liveTsUrlConverter, converter); + .setObjs(IOptionConstant.liveTsUrlConverter, converter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index 90112482..556244b1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -96,7 +96,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { throw new IllegalArgumentException("adapter为空"); } - getTaskWrapper().getOptionParams().setParams(IOptionConstant.fileLenAdapter, adapter); + getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); return this; } } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java index 544ece5d..976d12e4 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java @@ -83,16 +83,16 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { return; } - if (getConfig().isOpenDynamicFile) { + if (getThreadConfig().isOpenDynamicFile) { readDynamicFile(is); } else { readNormal(is); handleComplete(); } } catch (IOException e) { - fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true); } catch (Exception e) { - fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), false); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), false); } finally { try { if (is != null) { @@ -127,7 +127,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { ReadableByteChannel fic = null; try { int len; - fos = new FileOutputStream(getConfig().tempFile, true); + fos = new FileOutputStream(getThreadConfig().tempFile, true); foc = fos.getChannel(); fic = Channels.newChannel(is); ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); @@ -154,7 +154,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } handleComplete(); } catch (IOException e) { - fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true); } finally { try { if (fos != null) { @@ -179,7 +179,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { BufferedRandomAccessFile file = null; try { file = - new BufferedRandomAccessFile(getConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); + new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); file.seek(getThreadRecord().startLocation); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; @@ -201,7 +201,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } } } catch (IOException e) { - fail(new AriaIOException(TAG, String.format("下载失败【%s】", getConfig().url), e), true); + fail(new AriaIOException(TAG, String.format("下载失败【%s】", getThreadConfig().url), e), true); } finally { try { if (file != null) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index c988d83b..1f1a7ce0 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -66,9 +66,9 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } file = - new BufferedRandomAccessFile(getConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); + new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); if (getThreadRecord().startLocation != 0) { - //file.skipBytes((int) getConfig().START_LOCATION); + //file.skipBytes((int) getThreadConfig().START_LOCATION); file.seek(getThreadRecord().startLocation); } boolean complete = upload(client, file); @@ -81,7 +81,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } catch (IOException e) { fail(new AriaIOException(TAG, String.format("上传失败,filePath: %s, uploadUrl: %s", getEntity().getFilePath(), - getConfig().url)), true); + getThreadConfig().url)), true); } catch (Exception e) { fail(new AriaIOException(TAG, null, e), false); } finally { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index 65229f10..b34fb470 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -22,6 +22,8 @@ import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.core.group.AbsGroupUtil; @@ -46,8 +48,8 @@ public class DGroupLoaderUtil extends AbsGroupUtil { private boolean getLenComplete = false; private List mTempWrapper = new ArrayList<>(); - public DGroupLoaderUtil(IDGroupListener listener, DGTaskWrapper taskWrapper) { - super(listener, taskWrapper); + public DGroupLoaderUtil(AbsTaskWrapper taskWrapper, IEventListener listener) { + super(taskWrapper, listener); taskWrapper.generateTaskOption(HttpTaskOption.class); } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index 69b63cb2..bab8b614 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -61,7 +61,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { BufferedInputStream is = null; BufferedRandomAccessFile file = null; try { - URL url = ConnectionHelp.handleUrl(getConfig().url, mTaskOption); + URL url = ConnectionHelp.handleUrl(getThreadConfig().url, mTaskOption); conn = ConnectionHelp.handleConnection(url, mTaskOption); if (mTaskWrapper.isSupportBP()) { ALog.d(TAG, @@ -103,12 +103,12 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); if (mTaskOption.isChunked()) { readChunked(is); - } else if (getConfig().isOpenDynamicFile) { + } else if (getThreadConfig().isOpenDynamicFile) { readDynamicFile(is); } else { //创建可设置位置的文件 file = - new BufferedRandomAccessFile(getConfig().tempFile, "rwd", + new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); //设置每条线程写入文件的位置 file.seek(getThreadRecord().startLocation); @@ -150,7 +150,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { private void readChunked(InputStream is) { FileOutputStream fos = null; try { - fos = new FileOutputStream(getConfig().tempFile, true); + fos = new FileOutputStream(getThreadConfig().tempFile, true); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { @@ -167,7 +167,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { } catch (IOException e) { fail(new AriaIOException(TAG, String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), - getConfig().url)), true); + getThreadConfig().url)), true); } finally { if (fos != null) { try { @@ -188,7 +188,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { ReadableByteChannel fic = null; try { int len; - fos = new FileOutputStream(getConfig().tempFile, true); + fos = new FileOutputStream(getThreadConfig().tempFile, true); foc = fos.getChannel(); fic = Channels.newChannel(is); ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); @@ -219,7 +219,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { } catch (IOException e) { fail(new AriaIOException(TAG, String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), - getConfig().url), e), true); + getThreadConfig().url), e), true); } finally { try { if (fos != null) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java index 67e3b21d..3b20ba79 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpUThreadTaskAdapter.java @@ -62,7 +62,7 @@ final class HttpUThreadTaskAdapter extends BaseHttpThreadTaskAdapter { } URL url; try { - url = new URL(CommonUtil.convertUrl(getConfig().url)); + url = new URL(CommonUtil.convertUrl(getThreadConfig().url)); mHttpConn = (HttpURLConnection) url.openConnection(); mHttpConn.setRequestMethod(mTaskOption.getRequestEnum().name); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java index c2b97fcb..61cc43ed 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java @@ -24,7 +24,6 @@ import com.arialyy.aria.core.listener.BaseDListener; import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.task.AbsTask; -import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; /** @@ -32,7 +31,7 @@ import com.arialyy.aria.util.CommonUtil; */ public class M3U8Listener extends BaseDListener implements IDLoadListener { - M3U8Listener(AbsTask task, Handler outHandler) { + public M3U8Listener(AbsTask task, Handler outHandler) { super(task, outHandler); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index bad1cbfc..ba680258 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -15,10 +15,10 @@ */ package com.arialyy.aria.m3u8; -import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.inf.ITaskOption; -import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.processor.IVodTsUrlConverter; import java.lang.ref.SoftReference; import java.util.List; @@ -101,7 +101,7 @@ public class M3U8TaskOption implements ITaskOption { /** * 生成索引占位字段 */ - private boolean generateIndexFileTemp; + private boolean generateIndexFileTemp = false; public boolean isGenerateIndexFileTemp() { return generateIndexFileTemp; diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java index b4c2e4c6..dee64820 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java @@ -60,7 +60,7 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { HttpURLConnection conn = null; BufferedInputStream is = null; try { - URL url = ConnectionHelp.handleUrl(getConfig().url, mHttpTaskOption); + URL url = ConnectionHelp.handleUrl(getThreadConfig().url, mHttpTaskOption); conn = ConnectionHelp.handleConnection(url, mHttpTaskOption); ALog.d(TAG, String.format("分片【%s】开始下载", getThreadRecord().threadId)); ConnectionHelp.setConnectParam(mHttpTaskOption, conn); @@ -92,21 +92,21 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); if (mHttpTaskOption.isChunked()) { readChunked(is); - } else if (getConfig().isOpenDynamicFile) { + } else if (getThreadConfig().isOpenDynamicFile) { readDynamicFile(is); } } catch (MalformedURLException e) { fail(new TaskException(TAG, String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e), false); + getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), false); } catch (IOException e) { fail(new TaskException(TAG, String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e), true); + getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), true); } catch (Exception e) { fail(new TaskException(TAG, String.format("分片【%s】下载失败,filePath: %s, url: %s", getThreadRecord().threadId, - getConfig().tempFile.getPath(), getEntity().getUrl()), e), false); + getThreadConfig().tempFile.getPath(), getEntity().getUrl()), e), false); } finally { try { if (is != null) { @@ -127,7 +127,7 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { private void readChunked(InputStream is) { FileOutputStream fos = null; try { - fos = new FileOutputStream(getConfig().tempFile, true); + fos = new FileOutputStream(getThreadConfig().tempFile, true); byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { @@ -143,8 +143,8 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { handleComplete(); } catch (IOException e) { fail(new AriaIOException(TAG, - String.format("文件下载失败,savePath: %s, url: %s", getConfig().tempFile.getPath(), - getConfig().url), e), true); + String.format("文件下载失败,savePath: %s, url: %s", getThreadConfig().tempFile.getPath(), + getThreadConfig().url), e), true); } finally { if (fos != null) { try { @@ -165,7 +165,7 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { ReadableByteChannel fic = null; try { int len; - fos = new FileOutputStream(getConfig().tempFile, true); + fos = new FileOutputStream(getThreadConfig().tempFile, true); foc = fos.getChannel(); fic = Channels.newChannel(is); ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); @@ -186,8 +186,8 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { handleComplete(); } catch (IOException e) { fail(new AriaIOException(TAG, - String.format("文件下载失败,savePath: %s, url: %s", getConfig().tempFile.getPath(), - getConfig().url), e), true); + String.format("文件下载失败,savePath: %s, url: %s", getThreadConfig().tempFile.getPath(), + getThreadConfig().url), e), true); } finally { try { if (fos != null) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index 29581bc3..04346744 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -19,13 +19,15 @@ import android.text.TextUtils; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.processor.ILiveTsUrlConverter; import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.AbsLoader; import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; +import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoThread; import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8TaskOption; @@ -54,23 +56,28 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { private List mPeerUrls = new ArrayList<>(); private M3U8TaskOption mM3U8Option; - protected M3U8LiveUtil(DTaskWrapper wrapper, M3U8Listener listener) { + public M3U8LiveUtil(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); - wrapper.generateM3u8Option(M3U8TaskOption.class); - mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); + } + + @Override public DTaskWrapper getTaskWrapper() { + return (DTaskWrapper) super.getTaskWrapper(); } @Override protected AbsLoader createLoader() { - return new M3U8LiveLoader((M3U8Listener) getListener(), (DTaskWrapper) getTaskWrapper()); + getTaskWrapper().generateM3u8Option(M3U8TaskOption.class); + getTaskWrapper().generateTaskOption(HttpTaskOption.class); + mM3U8Option = (M3U8TaskOption) getTaskWrapper().getM3u8Option(); + return new M3U8LiveLoader((M3U8Listener) getListener(), getTaskWrapper()); } @Override protected Runnable createInfoThread() { return null; } - private Runnable createLiveInfoThread(){ + private Runnable createLiveInfoThread() { M3U8InfoThread infoThread = - new M3U8InfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + new M3U8InfoThread(getTaskWrapper(), new OnFileInfoCallback() { @Override public void onComplete(String key, CompleteInfo info) { ALog.d(TAG, "更新直播的m3u8文件"); } @@ -88,7 +95,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { ILiveTsUrlConverter converter = mM3U8Option.getLiveTsUrlConverter(); if (converter != null) { if (TextUtils.isEmpty(mM3U8Option.getBandWidthUrl())) { - url = converter.convert(((DownloadEntity) getTaskWrapper().getEntity()).getUrl(), url); + url = converter.convert(getTaskWrapper().getEntity().getUrl(), url); } else { url = converter.convert(mM3U8Option.getBandWidthUrl(), url); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java index abb28034..1a7a1ebe 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java @@ -19,13 +19,15 @@ import android.text.TextUtils; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.AbsLoader; import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; +import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoThread; import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8TaskOption; @@ -43,28 +45,32 @@ import java.util.List; */ public class M3U8VodUtil extends AbsNormalLoaderUtil { - private M3U8Listener mListener; private List mUrls = new ArrayList<>(); private M3U8TaskOption mM3U8Option; - public M3U8VodUtil(DTaskWrapper wrapper, M3U8Listener listener) { + public M3U8VodUtil(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); - wrapper.generateM3u8Option(M3U8TaskOption.class); - mListener = listener; - mM3U8Option = (M3U8TaskOption) wrapper.getM3u8Option(); + } + + @Override public DTaskWrapper getTaskWrapper() { + return (DTaskWrapper) super.getTaskWrapper(); } @Override protected AbsLoader createLoader() { - return new M3U8VodLoader((M3U8Listener) getListener(), (DTaskWrapper) getTaskWrapper()); + getTaskWrapper().generateM3u8Option(M3U8TaskOption.class); + getTaskWrapper().generateTaskOption(HttpTaskOption.class); + mM3U8Option = (M3U8TaskOption) getTaskWrapper().getM3u8Option(); + return new M3U8VodLoader((M3U8Listener) getListener(), getTaskWrapper()); } @Override protected Runnable createInfoThread() { - return new M3U8InfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + return new M3U8InfoThread(getTaskWrapper(), new OnFileInfoCallback() { @Override public void onComplete(String key, CompleteInfo info) { IVodTsUrlConverter converter = mM3U8Option.getVodUrlConverter(); if (converter != null) { if (TextUtils.isEmpty(mM3U8Option.getBandWidthUrl())) { - mUrls.addAll(converter.convert(getEntity().getUrl(), (List) info.obj)); + mUrls.addAll( + converter.convert(getTaskWrapper().getEntity().getUrl(), (List) info.obj)); } else { mUrls.addAll( converter.convert(mM3U8Option.getBandWidthUrl(), (List) info.obj)); @@ -81,7 +87,7 @@ public class M3U8VodUtil extends AbsNormalLoaderUtil { } mM3U8Option.setUrls(mUrls); if (isStop()) { - getListener().onStop(getEntity().getCurrentProgress()); + getListener().onStop(getTaskWrapper().getEntity().getCurrentProgress()); } else if (isCancel()) { getListener().onCancel(); } else { @@ -94,8 +100,4 @@ public class M3U8VodUtil extends AbsNormalLoaderUtil { } }); } - - private DownloadEntity getEntity() { - return (DownloadEntity) getTaskWrapper().getEntity(); - } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index bef20c95..367f1f71 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -78,8 +78,11 @@ public class RecordHandler implements IRecordHandler { if (!file.exists()) { ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); + mTaskRecord.threadRecords.clear(); + mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); initRecord(false); } else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { + mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); initRecord(false); } mAdapter.handlerTaskRecord(mTaskRecord); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index 8a66e24d..3774e083 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -23,6 +23,8 @@ import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IUtil; import com.arialyy.aria.core.listener.IDGroupListener; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.Map; @@ -49,12 +51,12 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { private DGTaskWrapper mGTWrapper; private GroupRunState mState; - protected AbsGroupUtil(IDGroupListener listener, DGTaskWrapper groupWrapper) { - mListener = listener; - mGTWrapper = groupWrapper; + protected AbsGroupUtil(AbsTaskWrapper groupWrapper, IEventListener listener) { + mListener = (IDGroupListener) listener; + mGTWrapper = (DGTaskWrapper) groupWrapper; mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval(); mState = new GroupRunState(groupWrapper.getKey(), mListener, - groupWrapper.getSubTaskWrapper().size(), mSubQueue); + mGTWrapper.getSubTaskWrapper().size(), mSubQueue); mScheduler = new Handler(Looper.getMainLooper(), SimpleSchedulers.newInstance(mState)); initState(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java index 6d882148..77db34a3 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java @@ -23,6 +23,7 @@ import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; @@ -34,14 +35,14 @@ import com.arialyy.aria.util.RecordUtil; * Created by Aria.Lao on 2017/7/20. 任务组下载事件 */ public class DownloadGroupListener - extends BaseListener + extends BaseListener> implements IDGroupListener { private GroupSendParams mSeedEntity; - public DownloadGroupListener(DownloadGroupTask task, Handler outHandler) { + public DownloadGroupListener(AbsTask task, Handler outHandler) { super(task, outHandler); mSeedEntity = new GroupSendParams<>(); - mSeedEntity.groupTask = task; + mSeedEntity.groupTask = (DownloadGroupTask) task; } @Override diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java index 8e4efe97..f9d9679a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java @@ -37,7 +37,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { /** * 当前线程的下去区间的进度 */ - private long mRangeProgress = 0; + private long mRangeProgress; private ThreadRecord mThreadRecord; private IThreadTaskObserver mObserver; private AbsTaskWrapper mWrapper; @@ -75,9 +75,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { return mThreadRecord; } - @Deprecated protected AbsTaskWrapper getTaskWrapper() { - // TODO: 2019-09-18 需要修改方法名称为:getWrapper return mWrapper; } @@ -95,9 +93,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { /** * 获取线程配置信息 */ - @Deprecated - protected SubThreadConfig getConfig() { - // TODO: 2019-09-18 需要修改方法名称为:getThreadConfig + protected SubThreadConfig getThreadConfig() { return mThreadConfig; } @@ -108,7 +104,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { @Override public void setMaxSpeed(int speed) { if (mSpeedBandUtil == null) { mSpeedBandUtil = - new BandwidthLimiter(getTaskConfig().getMaxSpeed(), getConfig().startThreadNum); + new BandwidthLimiter(getTaskConfig().getMaxSpeed(), getThreadConfig().startThreadNum); } mSpeedBandUtil.setMaxRate(speed); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index d711559a..464b9af6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -25,8 +25,8 @@ import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; @@ -62,16 +62,16 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private Handler mStateHandler; private SubThreadConfig mConfig; /** - * 当前子线程相对于总长度的位置 + * 当前线程的下去区间的进度 */ - protected long mChildCurrentLocation = 0; + private long mRangeProgress; private IThreadTaskAdapter mAdapter; - protected ThreadRecord mRecord; + private ThreadRecord mRecord; private Thread mConfigThread = new Thread(new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - final long currentTemp = mChildCurrentLocation; + final long currentTemp = mRangeProgress; writeConfig(false, currentTemp); } }); @@ -86,7 +86,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { mConfigThreadPool = Executors.newCachedThreadPool(); isNotNetRetry = AriaConfig.getInstance().getAConfig().isNotNetRetry(); - mChildCurrentLocation = mRecord.startLocation; + mRangeProgress = mRecord.startLocation; } /** @@ -94,6 +94,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { */ public void setAdapter(IThreadTaskAdapter adapter) { mAdapter = adapter; + mAdapter.setThreadStateObserver(this); } /** @@ -166,7 +167,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { public void breakTask() { taskBreak = true; if (mTaskWrapper.isSupportBP()) { - final long currentTemp = mChildCurrentLocation; + final long currentTemp = mRangeProgress; updateState(IThreadState.STATE_STOP, null); ALog.d(TAG, String.format("任务【%s】thread__%s__中断【停止位置:%s】", getFileName(), mRecord.threadId, currentTemp)); @@ -239,7 +240,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { ALog.i(TAG, String.format("任务【%s】已停止", getFileName())); } else { if (mTaskWrapper.isSupportBP()) { - final long stopLocation = mChildCurrentLocation; + final long stopLocation = mRangeProgress; ALog.d(TAG, String.format("任务【%s】thread__%s__停止【当前线程停止位置:%s】", getFileName(), mRecord.threadId, stopLocation)); @@ -295,20 +296,20 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * * @param needRetry 是否需要重试,一般是网络错误才需要重试 */ - @Override public void updateFailState(@Nullable BaseException e, boolean needRetry) { - + @Override public synchronized void updateFailState(@Nullable BaseException e, boolean needRetry) { + fail(mRangeProgress, e, needRetry); } @Override public synchronized void updateProgress(long len) { - mChildCurrentLocation += len; + mRangeProgress += len; Thread loopThread = mStateHandler.getLooper().getThread(); if (!loopThread.isAlive() || loopThread.isInterrupted()) { return; } mStateHandler.obtainMessage(IThreadState.STATE_RUNNING, len).sendToTarget(); if (System.currentTimeMillis() - mLastSaveTime > 5000 - && mChildCurrentLocation < mRecord.endLocation) { + && mRangeProgress < mRecord.endLocation) { mLastSaveTime = System.currentTimeMillis(); if (!mConfigThreadPool.isShutdown()) { mConfigThreadPool.execute(mConfigThread); @@ -327,16 +328,6 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { String.format("任务【%s】thread__%s__取消", getFileName(), mRecord.threadId)); } - /** - * 线程任务失败 - * - * @param subCurrentLocation 当前线程下载进度 - * @param ex 异常信息 - */ - protected void fail(long subCurrentLocation, BaseException ex) { - fail(subCurrentLocation, ex, true); - } - /** * 任务失败 * @@ -451,7 +442,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { /** * 发送失败信息 */ - protected void sendFailMsg(@Nullable BaseException e) { + private void sendFailMsg(@Nullable BaseException e) { if (e != null) { Bundle b = new Bundle(); b.putSerializable(IThreadState.KEY_ERROR_INFO, e); @@ -467,7 +458,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * @param isComplete 当前线程是否完成 {@code true}完成 * @param record 当前下载进度 */ - protected void writeConfig(boolean isComplete, final long record) { + private void writeConfig(boolean isComplete, final long record) { if (mRecord != null) { mRecord.isComplete = isComplete; if (mConfig.isBlock) { @@ -487,6 +478,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { isDestroy = false; Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode()); + mAdapter.call(this); return this; } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 37d93aee..600e6ae1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -20,10 +20,8 @@ import com.arialyy.aria.core.TaskOptionParams; import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.inf.IUtil; -import com.arialyy.aria.core.listener.BaseListener; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.task.AbsTask; -import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.lang.ref.SoftReference; @@ -42,8 +40,8 @@ public class ComponentUtil { public static final int COMPONENT_TYPE_FTP = 2; public static final int COMPONENT_TYPE_M3U8 = 3; - private static volatile ComponentUtil INSTANCE = null; private String TAG = CommonUtil.getClassName(getClass()); + private static volatile ComponentUtil INSTANCE = null; private ComponentUtil() { @@ -99,7 +97,7 @@ public class ComponentUtil { * * @return 返回下载工具,创建失败返回null */ - public T buildUtil(ITaskWrapper wrapper, IEventListener listener) { + public T buildUtil(AbsTaskWrapper wrapper, IEventListener listener) { int requestType = wrapper.getRequestType(); String className = null; switch (requestType) { @@ -122,10 +120,10 @@ public class ComponentUtil { className = "com.arialyy.aria.http.upload.HttpULoaderUtil"; break; case ITaskWrapper.D_FTP_DIR: - className = "com.arialyy.aria.http.download.DGroupLoaderUtil"; + className = "com.arialyy.aria.ftp.download.FtpDirDLoaderUtil"; break; case ITaskWrapper.DG_HTTP: - className = "com.arialyy.aria.ftp.download.FtpDirDLoaderUtil"; + className = "com.arialyy.aria.http.download.DGroupLoaderUtil"; break; } if (className == null) { @@ -158,7 +156,7 @@ public class ComponentUtil { * @param wrapperType 任务类型{@link ITaskWrapper} * @return 返回事件监听,如果创建失败返回null */ - public T buildListener(int wrapperType, ITask task, + public T buildListener(int wrapperType, AbsTask task, Handler outHandler) { String className = null, errorStr = "请添加FTP插件"; switch (wrapperType) { @@ -217,6 +215,7 @@ public class ComponentUtil { try { taskOption = clazz.newInstance(); for (Field field : fields) { + field.setAccessible(true); Class type = field.getType(); String key = field.getName(); if (type != SoftReference.class) { diff --git a/app/build.gradle b/app/build.gradle index 7d4bca30..4ffcbf6a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -73,6 +73,7 @@ dependencies { implementation 'com.github.bumptech.glide:glide:3.7.0' implementation 'com.pddstudio:highlightjs-android:1.5.0' implementation 'org.greenrobot:eventbus:3.1.1' + implementation project(path: ':M3U8Component') } repositories { mavenCentral() diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index 60086a2a..a42d1a1a 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -291,7 +291,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @Override public List convert(String m3u8Url, List tsUrls) { Uri uri = Uri.parse(m3u8Url); - String parentUrl = uri.getAuthority() + "://" + uri.getHost(); + String parentUrl = "http://" + uri.getHost(); List newUrls = new ArrayList<>(); for (String url : tsUrls) { newUrls.add(parentUrl + url); @@ -325,12 +325,6 @@ public class M3U8VodDLoadActivity extends BaseActivity { //}) .setTsUrlConvert(new IVodTsUrlConverter() { @Override public List convert(String m3u8Url, List tsUrls) { - //int index = m3u8Url.lastIndexOf("/"); - //String parentUrl = m3u8Url.substring(0, index + 1); - //List newUrls = new ArrayList<>(); - //for (String url : tsUrls) { - // newUrls.add(parentUrl + url); - //} Uri uri = Uri.parse(m3u8Url); String parentUrl = uri.getScheme() + "://" + uri.getHost(); List newUrls = new ArrayList<>(); @@ -350,16 +344,6 @@ public class M3U8VodDLoadActivity extends BaseActivity { .generateIndexFile() .controller(ControllerType.CREATE_CONTROLLER) .create(); - - Aria.download(M3U8VodDLoadActivity.this) - .load(mUrl) - .useServerFileName(true) - .setFilePath(mFilePath, true) - .asM3U8().setTsUrlConvert(new IVodTsUrlConverter() { - @Override public List convert(String m3u8Url, List tsUrls) { - return null; - } - }); } private Class c = BuilderController.class; diff --git a/app/src/main/res/layout/activity_m3u8_vod.xml b/app/src/main/res/layout/activity_m3u8_vod.xml index b8f3ad62..934d2aaf 100644 --- a/app/src/main/res/layout/activity_m3u8_vod.xml +++ b/app/src/main/res/layout/activity_m3u8_vod.xml @@ -77,6 +77,7 @@ /> Date: Wed, 9 Oct 2019 20:16:58 +0800 Subject: [PATCH 004/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dm3u8=E3=80=81http?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=AE=B0=E5=BD=95=E9=87=8D=E5=A4=8D=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arialyy/aria/http/HttpRecordAdapter.java | 7 +++++ .../com/arialyy/aria/m3u8/BaseM3U8Loader.java | 7 +++-- .../arialyy/aria/m3u8/M3U8RecordAdapter.java | 11 ++++++- .../aria/m3u8/live/M3U8LiveLoader.java | 9 +++--- .../arialyy/aria/m3u8/live/M3U8LiveUtil.java | 1 + .../core/common/AbsRecordHandlerAdapter.java | 3 ++ .../aria/core/common/RecordHandler.java | 1 + .../aria/core/inf/IRecordHandlerAdapter.java | 5 ++++ .../com/arialyy/aria/util/RecordUtil.java | 29 +++++++++++++------ .../download/m3u8/M3U8LiveDLoadActivity.java | 13 ++++++++- 10 files changed, 68 insertions(+), 18 deletions(-) diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java index 1955c1be..311ec0ed 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java @@ -36,6 +36,13 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { super(wrapper); } + @Override public void onPre() { + super.onPre(); + if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP){ + RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD); + } + } + @Override public void handlerTaskRecord(TaskRecord record) { RecordHelper helper = new RecordHelper(getWrapper(), record); if (record.isBlock) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java index c6b17fbe..91cb85f6 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.m3u8; +import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; @@ -127,8 +128,10 @@ public abstract class BaseM3U8Loader extends AbsLoader { } @Override protected IRecordHandler getRecordHandler(AbsTaskWrapper wrapper) { - - return null; + RecordHandler handler = new RecordHandler(wrapper); + M3U8RecordAdapter adapter = new M3U8RecordAdapter((DTaskWrapper) wrapper); + handler.setAdapter(adapter); + return handler; } protected DownloadEntity getEntity() { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 06193d55..25de650f 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -21,8 +21,10 @@ import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.RecordUtil; import java.io.File; import java.util.ArrayList; @@ -33,11 +35,18 @@ import java.util.ArrayList; public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { private M3U8TaskOption mOption; - public M3U8RecordAdapter(DTaskWrapper wrapper) { + M3U8RecordAdapter(DTaskWrapper wrapper) { super(wrapper); mOption = (M3U8TaskOption) wrapper.getM3u8Option(); } + @Override public void onPre() { + super.onPre(); + if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE){ + RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); + } + } + /** * 不处理live的记录 */ diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index b7433ca3..e7d7928d 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -52,19 +52,18 @@ public class M3U8LiveLoader extends BaseM3U8Loader { private static final int EXEC_MAX_NUM = 4; private Handler mStateHandler; private ArrayBlockingQueue mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); - private LiveStateManager mManager; private ReentrantLock LOCK = new ReentrantLock(); private Condition mCondition = LOCK.newCondition(); private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); - public M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) { + M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) { super(listener, wrapper); } @Override protected IThreadState createStateManager(Looper looper) { - mManager = new LiveStateManager(looper, mListener); - mStateHandler = new Handler(looper, mManager); - return mManager; + LiveStateManager manager = new LiveStateManager(looper, mListener); + mStateHandler = new Handler(looper, manager); + return manager; } void offerPeer(String peerUrl) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index 04346744..3620fc03 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -154,6 +154,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { mInfoPool.execute(mInfoThread); } }, 0, mM3U8Option.getLiveUpdateInterval(), TimeUnit.MILLISECONDS); + getLoader().start(); } private void closeTimer() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java index 139a5633..ee74b0e4 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java @@ -27,6 +27,9 @@ public abstract class AbsRecordHandlerAdapter implements IRecordHandlerAdapter { private AbsTaskWrapper mWrapper; protected String TAG = CommonUtil.getClassName(getClass()); + @Override public void onPre() { + } + public AbsRecordHandlerAdapter(AbsTaskWrapper wrapper) { mWrapper = wrapper; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index 367f1f71..ff15fc02 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -70,6 +70,7 @@ public class RecordHandler implements IRecordHandler { if (mConfigFile.exists()) { convertDb(); } else { + mAdapter.onPre(); mTaskRecord = DbDataHelper.getTaskRecord(getFilePath()); if (mTaskRecord == null) { initRecord(true); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java index 85e75f1b..e2a2992b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java @@ -26,6 +26,11 @@ import com.arialyy.aria.core.ThreadRecord; */ public interface IRecordHandlerAdapter { + /** + * 记录处理前的操作,可用来删除任务记录 + */ + void onPre(); + /** * 处理任务记录 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index ce8a9b58..a76cf20d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -16,16 +16,16 @@ package com.arialyy.aria.util; import android.text.TextUtils; -import com.arialyy.aria.core.inf.IRecordHandler; -import com.arialyy.aria.core.wrapper.RecordWrapper; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.RecordWrapper; import com.arialyy.aria.orm.DbEntity; import java.io.File; import java.util.List; @@ -148,13 +148,13 @@ public class RecordUtil { * 处理任务未完成的情况 */ if (!entity.isComplete()) { - if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { // 删除ts分片文件 + if (recordIsM3U8(record.taskType)) { // 删除ts分片文件 removeTsCache(targetFile, record.bandWidth); } else if (record.isBlock) { // 删除分块文件 removeBlockFile(record); } } else if (removeFile) { // 处理任务完成情况 - if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { + if (recordIsM3U8(record.taskType)) { removeTsCache(targetFile, record.bandWidth); } removeTargetFile(targetFile); @@ -164,6 +164,16 @@ public class RecordUtil { removeRecord(filePath); } + /** + * 任务记录是否是m3u8记录 + * + * @param recordType 任务记录类型 + * @return true 为m3u8任务 + */ + private static boolean recordIsM3U8(int recordType) { + return recordType == TaskRecord.TYPE_M3U8_VOD || recordType == TaskRecord.TYPE_M3U8_LIVE; + } + /** * 删除任务记录,默认删除文件 * @@ -208,13 +218,13 @@ public class RecordUtil { * 处理任务未完成的情况 */ if (!entity.isComplete()) { - if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { // 删除ts分片文件 + if (recordIsM3U8(record.taskType)) { // 删除ts分片文件 removeTsCache(targetFile, record.bandWidth); } else if (record.isBlock) { // 删除分块文件 removeBlockFile(record); } } else if (removeFile) { // 处理任务完成情况 - if (record.taskType == TaskRecord.TYPE_M3U8_VOD) { + if (recordIsM3U8(record.taskType)) { removeTsCache(targetFile, record.bandWidth); } removeTargetFile(targetFile); @@ -357,7 +367,8 @@ public class RecordUtil { tr.taskKey = newPath; File blockFile = new File(String.format(IRecordHandler.SUB_PATH, oldPath, tr.threadId)); if (blockFile.exists()) { - blockFile.renameTo(new File(String.format(IRecordHandler.SUB_PATH, newPath, tr.threadId))); + blockFile.renameTo( + new File(String.format(IRecordHandler.SUB_PATH, newPath, tr.threadId))); } } DbEntity.updateManyData(record.threadRecords); diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java index 69fcb184..0488f5bd 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java @@ -219,7 +219,18 @@ public class M3U8LiveDLoadActivity extends BaseActivity if (Aria.download(this).load(mEntity.getId()).isRunning()) { Aria.download(this).load(mEntity.getId()).stop(); } else { - Aria.download(this).load(mEntity.getId()).resume(); + Aria.download(this).load(mEntity.getId()) + .asM3U8() + .asLive() + .setLiveTsUrlConvert(new ILiveTsUrlConverter() { + @Override public String convert(String m3u8Url, String tsUrl) { + int index = m3u8Url.lastIndexOf("/"); + String parentUrl = m3u8Url.substring(0, index + 1); + return parentUrl + tsUrl; + } + }) + .controller(ControllerType.TASK_CONTROLLER) + .resume(); } break; case R.id.cancel: From ce408c0d5cf9fbed0f9f0bdddfc239b7317b3fd1 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 14 Oct 2019 20:00:06 +0800 Subject: [PATCH 005/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dftp=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/ftp/BaseFtpThreadTaskAdapter.java | 2 +- .../arialyy/aria/ftp/FtpDirInfoThread.java | 9 +++- .../aria/ftp/download/FtpDLoaderUtil.java | 7 +-- .../aria/ftp/download/FtpDirDLoaderUtil.java | 22 +++++--- ...LoaderUtil.java => FtpSubDLoaderUtil.java} | 8 ++- .../aria/ftp/upload/FtpULoaderUtil.java | 8 +-- .../aria/http/download/DGroupLoaderUtil.java | 4 +- ...oaderUtil.java => HttpSubDLoaderUtil.java} | 12 ++--- .../arialyy/aria/core/group/AbsGroupUtil.java | 6 +-- .../aria/core/listener/BaseUListener.java | 4 +- .../aria/core/processor/ProxyHandler.java | 50 +++++++++++++++++++ .../aria/core/wrapper/AbsTaskWrapper.java | 4 ++ app/build.gradle | 1 + .../core/download/FtpDownloadActivity.java | 9 ++-- .../core/download/FtpDownloadModule.java | 4 +- .../group/FTPDirDownloadActivity.java | 9 ++-- .../simple/core/upload/FtpUploadActivity.java | 23 +++------ .../simple/core/upload/UploadModule.java | 4 +- 18 files changed, 122 insertions(+), 64 deletions(-) rename FtpComponent/src/main/java/com/arialyy/aria/ftp/download/{SubDLoaderUtil.java => FtpSubDLoaderUtil.java} (86%) rename HttpComponent/src/main/java/com/arialyy/aria/http/download/{SubDLoaderUtil.java => HttpSubDLoaderUtil.java} (86%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/processor/ProxyHandler.java diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java index 6f639932..5c73fefc 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java @@ -43,7 +43,7 @@ public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter { protected BaseFtpThreadTaskAdapter(SubThreadConfig config) { super(config); - + mTaskOption = (FtpTaskOption) getTaskWrapper().getTaskOption(); } protected void closeClient(FTPClient client) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java index 2751524f..a355a01b 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java @@ -91,10 +91,17 @@ public class FtpDirInfoThread extends AbsFtpInfoThread> +public class BaseUListener extends BaseListener> implements IUploadListener { - BaseUListener(AbsTask task, Handler outHandler) { + public BaseUListener(AbsTask task, Handler outHandler) { super(task, outHandler); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ProxyHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ProxyHandler.java new file mode 100644 index 00000000..be6d2c10 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ProxyHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.processor; + +import com.arialyy.aria.core.inf.IEventHandler; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * @Author lyy + * @Date 2019-10-7 + * 处理器的动态代理 + */ +public class ProxyHandler implements InvocationHandler { + + private Object mTarget; + + /** + * 绑定代理对象并返回代理类 + */ + public Object bind(Class clazz) { + //绑定该类实现的所有接口,取得代理类 + try { + mTarget = clazz.newInstance(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } + return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); + } + + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(mTarget, args); + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index b97e8f61..a2b283e9 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -84,6 +84,10 @@ public abstract class AbsTaskWrapper */ private TaskOptionParams optionParams = new TaskOptionParams(); + public void setTaskOption(ITaskOption option) { + this.taskOption = option; + } + public ITaskOption getTaskOption() { return taskOption; } diff --git a/app/build.gradle b/app/build.gradle index 4ffcbf6a..fa553264 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,6 +74,7 @@ dependencies { implementation 'com.pddstudio:highlightjs-android:1.5.0' implementation 'org.greenrobot:eventbus:3.1.1' implementation project(path: ':M3U8Component') + implementation project(path: ':FtpComponent') } repositories { mavenCentral() diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 94d3a04e..5e9f8acd 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -48,7 +48,7 @@ public class FtpDownloadActivity extends BaseActivity getFtpDownloadInfo(Context context) { String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index 755e4b90..b8077dbb 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -35,10 +35,11 @@ import com.arialyy.simple.widget.SubStateLinearLayout; * Created by lyy on 2017/7/6. */ public class FTPDirDownloadActivity extends BaseActivity { - private static final String dir = "ftp://9.9.9.205:2121/upload/测试"; + private static final String dir = "ftp://9.9.9.50:2121/upload/测试"; private SubStateLinearLayout mChildList; private long mTaskId = -1; + private String user = "lao", pwd = "123456"; @Override protected void init(Bundle savedInstanceState) { super.init(savedInstanceState); @@ -78,7 +79,7 @@ public class FTPDirDownloadActivity extends BaseActivity { private String mUrl; private UploadModule mModule; private long mTaskId = -1; + private String user = "lao", pwd = "123456"; @Override protected void init(Bundle savedInstanceState) { setTile("D_FTP 文件上传"); @@ -117,20 +118,8 @@ public class FtpUploadActivity extends BaseActivity { mTaskId = Aria.upload(this) .loadFtp(mFilePath) .setUploadUrl(mUrl) - .setUploadInterceptor( - new IFtpUploadInterceptor() { - - @Override - public FtpInterceptHandler onIntercept(UploadEntity entity, - List fileList) { - FtpInterceptHandler.Builder builder = new FtpInterceptHandler.Builder(); - //builder.coverServerFile(); - builder.resetFileName("test.zip"); - return builder.build(); - } - }) .option() - .login("8L8e", "8guD") + .login(user, pwd) .controller(ControllerType.CREATE_CONTROLLER) .create(); getBinding().setStateStr(getString(R.string.stop)); @@ -143,7 +132,7 @@ public class FtpUploadActivity extends BaseActivity { Aria.upload(this) .loadFtp(mTaskId) .option() - .login("8L8e", "8guD") + .login(user, pwd) .controller(ControllerType.TASK_CONTROLLER) .resume(); getBinding().setStateStr(getString(R.string.stop)); diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index 7c5994c0..e88d07cc 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -35,9 +35,9 @@ public class UploadModule extends BaseViewModule { * 获取Ftp上传信息 */ LiveData getFtpInfo(Context context) { - String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.205:2121/aa/你好"); + String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.50:2121/aa/你好"); String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, - Environment.getExternalStorageDirectory().getPath() + "/AriaPrj.rar"); + Environment.getExternalStorageDirectory().getPath() + "/Download/PaNTFS15562.zip"); UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath); if (entity != null) { From df310b7fbea481d3b5901718958ef53fa3872c7e Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 16 Oct 2019 11:07:02 +0800 Subject: [PATCH 006/140] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9D=9E=E9=9D=99?= =?UTF-8?q?=E6=80=81=E7=9A=84=E6=88=90=E5=91=98=E7=B1=BB=E8=AD=A6=E5=91=8A?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{BaseDelegate.java => BaseOption.java} | 4 +- .../aria/core/common/FTPSDelegate.java | 2 +- .../arialyy/aria/core/common/FtpDelegate.java | 2 +- .../aria/core/common/HttpDelegate.java | 8 +-- .../aria/core/download/m3u8/M3U8Delegate.java | 8 ++- .../core/download/m3u8/M3U8LiveDelegate.java | 12 ++-- .../core/download/m3u8/M3U8VodDelegate.java | 4 +- .../download/target/GroupBuilderTarget.java | 6 +- .../download/target/HttpBuilderTarget.java | 3 +- .../aria/core/download/tcp/TcpDelegate.java | 4 +- .../core/upload/target/FtpBuilderTarget.java | 2 + .../aria/http/upload/HttpULoaderUtil.java | 2 +- .../java/com/arialyy/aria/util/CheckUtil.java | 43 +++----------- .../core/download/SingleTaskActivity.java | 28 +++++---- .../download/m3u8/M3U8LiveDLoadActivity.java | 59 ++++++++++--------- .../core/upload/HttpUploadActivity.java | 7 ++- py/upload.py | 2 +- 17 files changed, 93 insertions(+), 103 deletions(-) rename Aria/src/main/java/com/arialyy/aria/core/common/{BaseDelegate.java => BaseOption.java} (93%) diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java similarity index 93% rename from Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java index 439f0a40..c3858bfa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/BaseDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java @@ -23,12 +23,12 @@ import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.CommonUtil; -public abstract class BaseDelegate { +public abstract class BaseOption { protected final String TAG; protected TARGET mTarget; protected AbsTaskWrapper mWrapper; - public BaseDelegate(TARGET target, AbsTaskWrapper wrapper) { + public BaseOption(TARGET target, AbsTaskWrapper wrapper) { TAG = CommonUtil.getClassName(getClass()); mTarget = target; mWrapper = wrapper; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java index 1197c370..6660f84c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java @@ -28,7 +28,7 @@ import com.arialyy.aria.util.ALog; /** * D_FTP SSL/TSL 参数委托 */ -public class FTPSDelegate extends BaseDelegate { +public class FTPSDelegate extends BaseOption { private FtpUrlEntity mUrlEntity; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java index be681bd2..11c181ed 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java @@ -28,7 +28,7 @@ import com.arialyy.aria.util.ALog; /** * Created by laoyuyu on 2018/3/9. */ -public class FtpDelegate extends BaseDelegate { +public class FtpDelegate extends BaseOption { private static final String TAG = "FtpDelegate"; public FtpDelegate(TARGET target, AbsTaskWrapper wrapper) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java index 8e0d2505..a9288aa0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java @@ -18,19 +18,19 @@ package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import java.net.Proxy; import java.util.HashMap; import java.util.Map; /** - * HTTP协议处理 + * HTTP任务设置 */ -public class HttpDelegate extends BaseDelegate { +public class HttpDelegate extends BaseOption { private Map params; private Map headers; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java index 348cc805..7693ea16 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.download.m3u8; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseDelegate; +import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IOptionConstant; @@ -25,12 +25,13 @@ import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.ComponentUtil; /** * m3u8 委托 */ -public class M3U8Delegate extends BaseDelegate { +public class M3U8Delegate extends BaseOption { private DTaskWrapper mTaskWrapper; public M3U8Delegate(TARGET target, AbsTaskWrapper wrapper) { @@ -67,6 +68,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setMergeHandler(ITsMergeHandler handler) { + CheckUtil.checkMemberClass(handler.getClass()); mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.mergeHandler, handler); return this; } @@ -79,6 +81,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setTsUrlConvert(IVodTsUrlConverter converter) { + CheckUtil.checkMemberClass(converter.getClass()); mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.vodUrlConverter, converter); return this; } @@ -102,6 +105,7 @@ public class M3U8Delegate extends BaseDelegate */ @CheckResult(suggest = Suggest.TO_CONTROLLER) public M3U8Delegate setBandWidthUrlConverter(IBandWidthUrlConverter converter) { + CheckUtil.checkMemberClass(converter.getClass()); mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.bandWidthUrlConverter, converter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java index 70acc618..a8417898 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java @@ -16,19 +16,20 @@ package com.arialyy.aria.core.download.m3u8; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseDelegate; -import com.arialyy.aria.core.processor.ILiveTsUrlConverter; -import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; /** * m3u8直播参数设置 */ -public class M3U8LiveDelegate extends BaseDelegate { +public class M3U8LiveDelegate extends BaseOption { M3U8LiveDelegate(TARGET target, AbsTaskWrapper wrapper) { super(target, wrapper); @@ -43,6 +44,7 @@ public class M3U8LiveDelegate extends BaseDelegate setLiveTsUrlConvert(ILiveTsUrlConverter converter) { + CheckUtil.checkMemberClass(converter.getClass()); ((DTaskWrapper) getTaskWrapper()).getM3U8Params() .setObjs(IOptionConstant.liveTsUrlConverter, converter); return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java index b0c9e880..b1fca565 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.download.m3u8; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseDelegate; +import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.event.EventMsgUtil; @@ -30,7 +30,7 @@ import com.arialyy.aria.util.ALog; /** * m3u8点播文件参数设置 */ -public class M3U8VodDelegate extends BaseDelegate { +public class M3U8VodDelegate extends BaseOption { private DTaskWrapper mTaskWrapper; M3U8VodDelegate(TARGET target, AbsTaskWrapper wrapper) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index b44f37a4..0bd189ca 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -17,14 +17,15 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; import java.util.List; /** @@ -151,6 +152,7 @@ public class GroupBuilderTarget extends AbsBuilderTarget { if (adapter == null) { throw new IllegalArgumentException("adapter为空"); } + CheckUtil.checkMemberClass(adapter.getClass()); getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index 556244b1..ff3a81ef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -24,6 +24,7 @@ import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.CheckUtil; public class HttpBuilderTarget extends AbsBuilderTarget { @@ -95,7 +96,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { if (adapter == null) { throw new IllegalArgumentException("adapter为空"); } - + CheckUtil.checkMemberClass(adapter.getClass()); getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java index 6bbc83c0..f77632ef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.download.tcp; import android.text.TextUtils; import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseDelegate; +import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -28,7 +28,7 @@ import java.nio.charset.Charset; * @Author aria * @Date 2019-09-06 */ -public class TcpDelegate extends BaseDelegate { +public class TcpDelegate extends BaseOption { private TcpTaskConfig mTcpConfig; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index 5dd3d981..e9c1119e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -22,6 +22,7 @@ import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.FtpDelegate; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.CheckUtil; /** * Created by Aria.Lao on 2017/7/27. @@ -52,6 +53,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget setUploadInterceptor(@NonNull IFtpUploadInterceptor uploadInterceptor) { + CheckUtil.checkMemberClass(uploadInterceptor.getClass()); return mConfigHandler.setUploadInterceptor(uploadInterceptor); } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java index be15e5ac..9db99f17 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderUtil.java @@ -27,7 +27,7 @@ import com.arialyy.aria.http.HttpTaskOption; * @Date 2019-09-19 */ public class HttpULoaderUtil extends AbsNormalLoaderUtil { - protected HttpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { + public HttpULoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); wrapper.generateTaskOption(HttpTaskOption.class); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 345c6560..8e786b6a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -19,11 +19,11 @@ package com.arialyy.aria.util; import android.text.TextUtils; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.exception.ParamException; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import java.io.File; +import java.lang.reflect.Modifier; import java.util.List; /** @@ -34,26 +34,15 @@ public class CheckUtil { private static final String TAG = "CheckUtil"; /** - * 检查ftp上传路径,如果ftp上传路径为空,抛出空指针异常 - * 如果ftp上传路径不是以"ftp"或"sftp",抛出参数异常 - * - * @param ftpUrl ftp上传路径 + * 检查成员类是否是静态和public */ - public static void checkFtpUploadUrl(String ftpUrl) { - if (TextUtils.isEmpty(ftpUrl)) { - throw new ParamException("ftp上传路径为空"); - } else if (!ftpUrl.startsWith("ftp") || !ftpUrl.startsWith("sftp")) { - throw new ParamException("ftp上传路径无效"); + public static void checkMemberClass(Class clazz) { + int modifiers = clazz.getModifiers(); + if (!clazz.isMemberClass() || !Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers)) { + ALog.e(TAG, "为了放置内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); } } - /** - * 判空 - */ - public static void checkNull(Object obj) { - if (obj == null) throw new IllegalArgumentException("不能传入空对象"); - } - /** * 检查分页数据,需要查询的页数,从1开始,如果page小于1 或 num 小于1,则抛出{@link NullPointerException} * @@ -64,15 +53,6 @@ public class CheckUtil { if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); } - /** - * 检查下载实体 - */ - public static void checkDownloadEntity(DownloadEntity entity) { - checkUrlInvalidThrow(entity.getUrl()); - entity.setUrl(entity.getUrl()); - checkPath(entity.getDownloadPath()); - } - /** * 检测下载链接是否为null */ @@ -150,15 +130,6 @@ public class CheckUtil { } } - /** - * 检查下载任务组保存路径 - */ - public static void checkDownloadPaths(List paths) { - if (paths == null || paths.isEmpty()) { - throw new IllegalArgumentException("链接保存路径不能为null"); - } - } - /** * 检测上传地址是否为null */ diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 88ec8680..5f35c7ad 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -31,11 +31,11 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -291,18 +291,7 @@ public class SingleTaskActivity extends BaseActivity { .load(mUrl) .useServerFileName(true) .setFilePath(mFilePath, true) - .setFileLenAdapter(new IHttpFileLenAdapter() { - @Override public long handleFileLen(Map> headers) { - - List sLength = headers.get("Content-Length"); - if (sLength == null || sLength.isEmpty()) { - return -1; - } - String temp = sLength.get(0); - - return Long.parseLong(temp); - } - }) + .setFileLenAdapter(new FileLenAdapter()) .option() .addHeader("1", "@") .controller(ControllerType.CREATE_CONTROLLER) @@ -327,4 +316,17 @@ public class SingleTaskActivity extends BaseActivity { mModule.updateFilePath(this, String.valueOf(data)); } } + + static class FileLenAdapter implements IHttpFileLenAdapter { + @Override public long handleFileLen(Map> headers) { + + List sLength = headers.get("Content-Length"); + if (sLength == null || sLength.isEmpty()) { + return -1; + } + String temp = sLength.get(0); + + return Long.parseLong(temp); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java index 0488f5bd..e9020c41 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java @@ -212,27 +212,28 @@ public class M3U8LiveDLoadActivity extends BaseActivity public void onClick(View view) { switch (view.getId()) { case R.id.start: - if (!AppUtil.chekEntityValid(mEntity)) { - startD(); - break; - } - if (Aria.download(this).load(mEntity.getId()).isRunning()) { - Aria.download(this).load(mEntity.getId()).stop(); - } else { - Aria.download(this).load(mEntity.getId()) - .asM3U8() - .asLive() - .setLiveTsUrlConvert(new ILiveTsUrlConverter() { - @Override public String convert(String m3u8Url, String tsUrl) { - int index = m3u8Url.lastIndexOf("/"); - String parentUrl = m3u8Url.substring(0, index + 1); - return parentUrl + tsUrl; - } - }) - .controller(ControllerType.TASK_CONTROLLER) - .resume(); - } - break; + startD(); + //if (!AppUtil.chekEntityValid(mEntity)) { + // startD(); + // break; + //} + //if (Aria.download(this).load(mEntity.getId()).isRunning()) { + // Aria.download(this).load(mEntity.getId()).stop(); + //} else { + // Aria.download(this).load(mEntity.getId()) + // .asM3U8() + // .asLive() + // .setLiveTsUrlConvert(new ILiveTsUrlConverter() { + // @Override public String convert(String m3u8Url, String tsUrl) { + // int index = m3u8Url.lastIndexOf("/"); + // String parentUrl = m3u8Url.substring(0, index + 1); + // return parentUrl + tsUrl; + // } + // }) + // .controller(ControllerType.TASK_CONTROLLER) + // .resume(); + //} + //break; case R.id.cancel: if (AppUtil.chekEntityValid(mEntity)) { Aria.download(this).load(mEntity.getId()).cancel(true); @@ -254,13 +255,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity // } //}) .asLive() - .setLiveTsUrlConvert(new ILiveTsUrlConverter() { - @Override public String convert(String m3u8Url, String tsUrl) { - int index = m3u8Url.lastIndexOf("/"); - String parentUrl = m3u8Url.substring(0, index + 1); - return parentUrl + tsUrl; - } - }) + .setLiveTsUrlConvert(new LiveTsUrlConverter()) .controller(ControllerType.CREATE_CONTROLLER) //.setLiveTsUrlConvert(new IVodTsUrlConverter() { // @Override public List convert(String m3u8Url, List tsUrls) { @@ -285,4 +280,12 @@ public class M3U8LiveDLoadActivity extends BaseActivity mModule.updateFilePath(this, String.valueOf(data)); } } + + static class LiveTsUrlConverter implements ILiveTsUrlConverter { + @Override public String convert(String m3u8Url, String tsUrl) { + int index = m3u8Url.lastIndexOf("/"); + String parentUrl = m3u8Url.substring(0, index + 1); + return parentUrl + tsUrl; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java index 49eb0a00..a4bd69bb 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java @@ -17,13 +17,14 @@ package com.arialyy.simple.core.upload; import android.os.Bundle; +import android.os.Environment; import android.view.View; import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.controller.ControllerType; -import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.task.UploadTask; +import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.frame.util.FileUtil; import com.arialyy.frame.util.show.L; import com.arialyy.simple.R; @@ -40,7 +41,9 @@ public class HttpUploadActivity extends BaseActivity { private static final String TAG = "HttpUploadActivity"; HorizontalProgressBarWithNumber mPb; - private final String FILE_PATH = "/mnt/sdcard/ggsg14.apk"; + //private final String FILE_PATH = "/mnt/sdcard/ggsg14.apk"; + private final String FILE_PATH = + Environment.getExternalStorageDirectory().getPath() + "/Download/PaNTFS15562.zip"; private UploadEntity mEntity; @Override protected int setLayoutId() { diff --git a/py/upload.py b/py/upload.py index 884b337e..eda21a2c 100644 --- a/py/upload.py +++ b/py/upload.py @@ -6,7 +6,7 @@ from werkzeug import secure_filename ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif', 'rar', 'apk']) app = Flask(__name__) -app.config['UPLOAD_FOLDER'] = 'd:/db/' +app.config['UPLOAD_FOLDER'] = '/Users/aria/temp/test/' app.config['MAX_CONTENT_LENGTH'] = 1600 * 1024 * 1024 """ From 550455084f96232b92c82cf0f3823922c298c787 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 16 Oct 2019 20:14:44 +0800 Subject: [PATCH 007/140] =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Aria/build.gradle | 2 +- .../aria/core/common/AbsNormalTarget.java | 8 +- .../arialyy/aria/core/common/BaseOption.java | 52 ------- .../aria/core/common/FTPSDelegate.java | 114 -------------- .../arialyy/aria/core/common/FtpDelegate.java | 99 ------------ .../arialyy/aria/core/common/FtpOption.java | 146 ++++++++++++++++++ .../{HttpDelegate.java => HttpOption.java} | 42 ++--- .../aria/core/download/m3u8/M3U8Delegate.java | 128 --------------- ...8LiveDelegate.java => M3U8LiveOption.java} | 38 ++--- .../aria/core/download/m3u8/M3U8Option.java | 104 +++++++++++++ .../core/download/m3u8/M3U8VodDelegate.java | 116 -------------- .../core/download/m3u8/M3U8VodOption.java | 87 +++++++++++ .../download/target/FtpBuilderTarget.java | 16 +- .../download/target/FtpDirBuilderTarget.java | 18 ++- .../download/target/FtpDirNormalTarget.java | 16 +- .../core/download/target/FtpNormalTarget.java | 16 +- .../download/target/GroupBuilderTarget.java | 10 +- .../download/target/GroupNormalTarget.java | 12 +- .../download/target/HttpBuilderTarget.java | 38 ++++- .../download/target/HttpNormalTarget.java | 42 ++++- .../download/target/M3U8NormalTarget.java | 56 +++++++ .../download/target/TcpBuilderTarget.java | 16 +- .../aria/core/download/tcp/TcpDelegate.java | 40 ++--- .../core/upload/target/FtpBuilderTarget.java | 16 +- .../core/upload/target/FtpNormalTarget.java | 17 +- .../core/upload/target/HttpBuilderTarget.java | 11 +- .../core/upload/target/HttpNormalTarget.java | 10 +- .../upload/target/UNormalConfigHandler.java | 2 - .../arialyy/aria/core/TaskOptionParams.java | 35 ++++- .../arialyy/aria/core/common/BaseOption.java | 26 ++++ .../com/arialyy/aria/util/CommonUtil.java | 9 -- .../core/download/FtpDownloadActivity.java | 21 ++- .../core/download/SingleTaskActivity.java | 7 +- .../group/FTPDirDownloadActivity.java | 16 +- .../download/m3u8/M3U8LiveDLoadActivity.java | 80 +++++----- .../download/m3u8/M3U8VodDLoadActivity.java | 99 ++++++------ .../simple/core/upload/FtpUploadActivity.java | 19 ++- .../core/upload/HttpUploadActivity.java | 10 +- .../arialyy/simple/modlue/AnyRunnModule.java | 17 +- 39 files changed, 802 insertions(+), 809 deletions(-) delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java rename Aria/src/main/java/com/arialyy/aria/core/common/{HttpDelegate.java => HttpOption.java} (62%) delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java rename Aria/src/main/java/com/arialyy/aria/core/download/m3u8/{M3U8LiveDelegate.java => M3U8LiveOption.java} (50%) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java delete mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/BaseOption.java diff --git a/Aria/build.gradle b/Aria/build.gradle index 08cb9b35..0c8ad2ce 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -28,7 +28,7 @@ dependencies { api project(':AriaAnnotations') api project(path: ':PublicComponent') - api 'com.arialyy.aria:aria-ftp-plug:1.0.4' +// api 'com.arialyy.aria:aria-ftp-plug:1.0.4' implementation project(path: ':HttpComponent') // 打包时用这个 // compile project(':AriaFtpPlug') diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index 0a4e505b..110c18f0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -36,14 +36,18 @@ public abstract class AbsNormalTarget extends Ab * * @return {@code true} 任务正在执行 */ - public abstract boolean isRunning(); + public boolean isRunning() { + return false; + } /** * 任务是否存在 * * @return {@code true} 任务存在 */ - public abstract boolean taskExists(); + public boolean taskExists() { + return false; + } private NormalController mNormalController; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java deleted file mode 100644 index c3858bfa..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/common/BaseOption.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.common; - -import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.controller.ControllerType; -import com.arialyy.aria.core.common.controller.FeatureController; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.util.CommonUtil; - -public abstract class BaseOption { - protected final String TAG; - protected TARGET mTarget; - protected AbsTaskWrapper mWrapper; - - public BaseOption(TARGET target, AbsTaskWrapper wrapper) { - TAG = CommonUtil.getClassName(getClass()); - mTarget = target; - mWrapper = wrapper; - } - - protected AbsTaskWrapper getTaskWrapper() { - return mWrapper; - } - - /** - * 使用对应等控制器,注意: - * 1、对于不存在的任务(第一次下载),只能使用{@link ControllerType#CREATE_CONTROLLER} - * 2、对于已存在的任务,只能使用{@link ControllerType#TASK_CONTROLLER} - * - * @param clazz {@link ControllerType#CREATE_CONTROLLER}、{@link ControllerType#TASK_CONTROLLER} - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public synchronized T controller(@ControllerType Class clazz) { - return FeatureController.newInstance(clazz, getTaskWrapper()); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java deleted file mode 100644 index 6660f84c..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FTPSDelegate.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.common; - -import android.text.TextUtils; -import androidx.annotation.CheckResult; -import com.arialyy.aria.core.FtpUrlEntity; -import com.arialyy.aria.core.ProtocolType; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.util.ALog; - -/** - * D_FTP SSL/TSL 参数委托 - */ -public class FTPSDelegate extends BaseOption { - - private FtpUrlEntity mUrlEntity; - - public FTPSDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - mUrlEntity = (FtpUrlEntity) getTaskWrapper().getOptionParams() - .getParam(IOptionConstant.ftpUrlEntity); - if (mUrlEntity != null) { - mUrlEntity.isFtps = true; - } - } - - /** - * 设置协议类型 - * - * @param protocol {@link ProtocolType} - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate setProtocol(@ProtocolType String protocol) { - if (TextUtils.isEmpty(protocol)) { - ALog.e(TAG, "设置协议失败,协议信息为空"); - return this; - } - mUrlEntity.protocol = protocol; - return this; - } - - /** - * 设置证书别名 - * - * @param keyAlias 别名 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate setAlias(String keyAlias) { - if (TextUtils.isEmpty(keyAlias)) { - ALog.e(TAG, "设置证书别名失败,证书别名为空"); - return this; - } - mUrlEntity.keyAlias = keyAlias; - return this; - } - - /** - * 设置证书密码 - * - * @param storePass 私钥密码 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate setStorePass(String storePass) { - if (TextUtils.isEmpty(storePass)) { - ALog.e(TAG, "设置证书密码失败,证书密码为空"); - return this; - } - mUrlEntity.storePass = storePass; - return this; - } - - /** - * 设置证书路径 - * - * @param storePath 证书路径 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate setStorePath(String storePath) { - if (TextUtils.isEmpty(storePath)) { - ALog.e(TAG, "设置证书路径失败,证书路径为空"); - return this; - } - mUrlEntity.storePath = storePath; - return this; - } - - /** - * 设置安全模式,默认true - * - * @param isImplicit true 隐式,false 显式 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate setImplicit(boolean isImplicit) { - mUrlEntity.isImplicit = isImplicit; - return this; - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java deleted file mode 100644 index 11c181ed..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpDelegate.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.common; - -import android.text.TextUtils; -import androidx.annotation.CheckResult; -import aria.apache.commons.net.ftp.FTPClientConfig; -import com.arialyy.aria.core.FtpUrlEntity; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.util.ALog; - -/** - * Created by laoyuyu on 2018/3/9. - */ -public class FtpDelegate extends BaseOption { - private static final String TAG = "FtpDelegate"; - - public FtpDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - } - - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FtpDelegate charSet(String charSet) { - if (TextUtils.isEmpty(charSet)) { - throw new NullPointerException("字符编码为空"); - } - getTaskWrapper().getOptionParams().setParams(IOptionConstant.charSet, charSet); - return this; - } - - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FtpDelegate login(String userName, String password) { - return login(userName, password, null); - } - - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FtpDelegate login(String userName, String password, String account) { - if (TextUtils.isEmpty(userName)) { - ALog.e(TAG, "用户名不能为null"); - return this; - } else if (TextUtils.isEmpty(password)) { - ALog.e(TAG, "密码不能为null"); - return this; - } - // urlEntity 不能在构造函数中获取,因为ftp上传时url是后于构造函数的 - FtpUrlEntity urlEntity = - (FtpUrlEntity) getTaskWrapper().getOptionParams() - .getParam(IOptionConstant.ftpUrlEntity); - if (urlEntity == null) { - ALog.e(TAG, "设置登陆信息失败,FtpUrlEntity为空"); - return this; - } - urlEntity.needLogin = true; - urlEntity.user = userName; - urlEntity.password = password; - urlEntity.account = account; - return this; - } - - /** - * 是否是FTPS协议 - * 如果是FTPS协议,需要使用{@link FTPSDelegate#setStorePath(String)} 、{@link FTPSDelegate#setAlias(String)} - * 设置证书信息 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FTPSDelegate asFtps() { - return new FTPSDelegate<>(mTarget, mWrapper); - } - - /** - * 配置ftp客户端信息 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public FtpDelegate setFtpClentConfig(FTPClientConfig config) { - getTaskWrapper().getOptionParams().setParams(IOptionConstant.clientConfig, config); - return this; - } - - //@Override public TARGET setProxy(Proxy proxy) { - // mTarget.getTaskWrapper().asFtp().setProxy(proxy); - // return mTarget; - //} -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java new file mode 100644 index 00000000..ff5c1b52 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +import android.text.TextUtils; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.ProtocolType; +import com.arialyy.aria.util.ALog; + +/** + * Created by laoyuyu on 2018/3/9. + */ +public class FtpOption extends BaseOption { + + private String charSet, userName, password, account; + private boolean isNeedLogin = false; + private FtpUrlEntity ftpUrlEntity; + private String protocol, keyAlias, storePass, storePath; + private boolean isImplicit = true; + + public FtpOption() { + super(); + } + + public FtpOption charSet(String charSet) { + if (TextUtils.isEmpty(charSet)) { + throw new NullPointerException("字符编码为空"); + } + this.charSet = charSet; + return this; + } + + public FtpOption login(String userName, String password) { + return login(userName, password, null); + } + + public FtpOption login(String userName, String password, String account) { + if (TextUtils.isEmpty(userName)) { + ALog.e(TAG, "用户名不能为null"); + return this; + } else if (TextUtils.isEmpty(password)) { + ALog.e(TAG, "密码不能为null"); + return this; + } + this.userName = userName; + this.password = password; + this.account = account; + isNeedLogin = true; + return this; + } + + /** + * 设置协议类型 + * + * @param protocol {@link ProtocolType} + */ + public FtpOption setProtocol(@ProtocolType String protocol) { + if (TextUtils.isEmpty(protocol)) { + ALog.e(TAG, "设置协议失败,协议信息为空"); + return this; + } + this.protocol = protocol; + return this; + } + + /** + * 设置证书别名 + * + * @param keyAlias 别名 + */ + public FtpOption setAlias(String keyAlias) { + if (TextUtils.isEmpty(keyAlias)) { + ALog.e(TAG, "设置证书别名失败,证书别名为空"); + return this; + } + this.keyAlias = keyAlias; + return this; + } + + /** + * 设置证书密码 + * + * @param storePass 私钥密码 + */ + public FtpOption setStorePass(String storePass) { + if (TextUtils.isEmpty(storePass)) { + ALog.e(TAG, "设置证书密码失败,证书密码为空"); + return this; + } + this.storePass = storePass; + return this; + } + + /** + * 设置证书路径 + * + * @param storePath 证书路径 + */ + public FtpOption setStorePath(String storePath) { + if (TextUtils.isEmpty(storePath)) { + ALog.e(TAG, "设置证书路径失败,证书路径为空"); + return this; + } + this.storePath = storePath; + return this; + } + + /** + * 设置安全模式,默认true + * + * @param isImplicit true 隐式,false 显式 + */ + public FtpOption setImplicit(boolean isImplicit) { + this.isImplicit = isImplicit; + return this; + } + + public void setFtpUrlEntity(FtpUrlEntity ftpUrlEntity) { + this.ftpUrlEntity = ftpUrlEntity; + ftpUrlEntity.needLogin = isNeedLogin; + ftpUrlEntity.user = userName; + ftpUrlEntity.password = password; + ftpUrlEntity.account = account; + if (!TextUtils.isEmpty(storePath)) { + ftpUrlEntity.isFtps = true; + ftpUrlEntity.protocol = protocol; + ftpUrlEntity.keyAlias = keyAlias; + ftpUrlEntity.storePass = storePass; + ftpUrlEntity.storePath = storePath; + ftpUrlEntity.isImplicit = isImplicit; + } + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java similarity index 62% rename from Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java index a9288aa0..0eb46613 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/HttpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java @@ -18,10 +18,7 @@ package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import java.net.Proxy; import java.util.HashMap; @@ -30,13 +27,16 @@ import java.util.Map; /** * HTTP任务设置 */ -public class HttpDelegate extends BaseOption { +public class HttpOption extends BaseOption { private Map params; private Map headers; + private RequestEnum requestEnum = RequestEnum.GET; + private Map formFields; + private Proxy proxy; - public HttpDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); + public HttpOption() { + super(); } /** @@ -45,29 +45,26 @@ public class HttpDelegate extends BaseOption { * @param requestEnum {@link RequestEnum} */ @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate setRequestType(RequestEnum requestEnum) { - getTaskWrapper().getOptionParams().setParams(IOptionConstant.requestEnum, requestEnum); + public HttpOption setRequestType(RequestEnum requestEnum) { + this.requestEnum = requestEnum; return this; } /** * 设置http请求参数 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate setParams(Map params) { + public HttpOption setParams(Map params) { if (this.params == null) { this.params = new HashMap<>(); } this.params.putAll(params); - getTaskWrapper().getOptionParams().setParams(IOptionConstant.params, this.params); return this; } /** * 设置http请求参数 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate setParam(String key, String value) { + public HttpOption setParam(String key, String value) { if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) { ALog.d(TAG, "key 或value 为空"); return this; @@ -76,16 +73,14 @@ public class HttpDelegate extends BaseOption { params = new HashMap<>(); } params.put(key, value); - getTaskWrapper().getOptionParams().setParams(IOptionConstant.params, params); return this; } /** * 设置http表单字段 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate setFormFields(Map params) { - getTaskWrapper().getOptionParams().setParams(IOptionConstant.formFields, params); + public HttpOption setFormFields(Map formFields) { + this.formFields = formFields; return this; } @@ -96,8 +91,7 @@ public class HttpDelegate extends BaseOption { * @param key header对应的key * @param value header对应的value */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate addHeader(@NonNull String key, @NonNull String value) { + public HttpOption addHeader(@NonNull String key, @NonNull String value) { if (TextUtils.isEmpty(key)) { ALog.w(TAG, "设置header失败,header对应的key不能为null"); return this; @@ -109,7 +103,6 @@ public class HttpDelegate extends BaseOption { this.headers = new HashMap<>(); } this.headers.put(key, value); - getTaskWrapper().getOptionParams().setParams(IOptionConstant.headers, this.headers); return this; } @@ -119,8 +112,7 @@ public class HttpDelegate extends BaseOption { * * @param headers 一组http header数据 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate addHeaders(@NonNull Map headers) { + public HttpOption addHeaders(@NonNull Map headers) { if (headers.size() == 0) { ALog.w(TAG, "设置header失败,map没有header数据"); return this; @@ -129,16 +121,14 @@ public class HttpDelegate extends BaseOption { this.headers = new HashMap<>(); } this.headers.putAll(headers); - getTaskWrapper().getOptionParams().setParams(IOptionConstant.headers, this.headers); return this; } /** * 设置代理 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public HttpDelegate setUrlProxy(Proxy proxy) { - getTaskWrapper().getOptionParams().setParams(IOptionConstant.proxy, proxy); + public HttpOption setUrlProxy(Proxy proxy) { + this.proxy = proxy; return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java deleted file mode 100644 index 7693ea16..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Delegate.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.download.m3u8; - -import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseOption; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.processor.IBandWidthUrlConverter; -import com.arialyy.aria.core.processor.ITsMergeHandler; -import com.arialyy.aria.core.processor.IVodTsUrlConverter; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.util.ComponentUtil; - -/** - * m3u8 委托 - */ -public class M3U8Delegate extends BaseOption { - private DTaskWrapper mTaskWrapper; - - public M3U8Delegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8); - mTaskWrapper = (DTaskWrapper) getTaskWrapper(); - mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_VOD); - } - - /** - * 生成m3u8索引文件 - * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate generateIndexFile() { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.generateIndexFileTemp, true); - return this; - } - - /** - * 是否合并ts文件,默认合并ts - * - * @param merge {@code true}合并所有ts文件为一个 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate merge(boolean merge) { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.mergeFile, merge); - return this; - } - - /** - * 如果你希望使用自行处理ts文件的合并,可以使用{@link ITsMergeHandler}处理ts文件的合并 - * 需要注意的是:只有{@link #merge(boolean)}设置合并ts文件,该方法才会生效 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate setMergeHandler(ITsMergeHandler handler) { - CheckUtil.checkMemberClass(handler.getClass()); - mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.mergeHandler, handler); - return this; - } - - /** - * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 - * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 - * - * @param converter {@link IVodTsUrlConverter} - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate setTsUrlConvert(IVodTsUrlConverter converter) { - CheckUtil.checkMemberClass(converter.getClass()); - mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.vodUrlConverter, converter); - return this; - } - - /** - * 选择需要下载的码率,默认下载的码率 - * - * @param bandWidth 指定的码率 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate setBandWidth(int bandWidth) { - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.bandWidth, bandWidth); - return this; - } - - /** - * M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的, - * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 - * - * @param converter {@link IBandWidthUrlConverter} - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8Delegate setBandWidthUrlConverter(IBandWidthUrlConverter converter) { - CheckUtil.checkMemberClass(converter.getClass()); - mTaskWrapper.getM3U8Params().setObjs(IOptionConstant.bandWidthUrlConverter, converter); - return this; - } - - /** - * 处理点播文件的下载参数 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8VodDelegate asVod() { - return new M3U8VodDelegate<>(mTarget, mTaskWrapper); - } - - /** - * 处理直播类的下载 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8LiveDelegate asLive() { - return new M3U8LiveDelegate<>(mTarget, mTaskWrapper); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java similarity index 50% rename from Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java rename to Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java index a8417898..6d5d000a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java @@ -15,54 +15,46 @@ */ package com.arialyy.aria.core.download.m3u8; -import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseOption; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.processor.ILiveTsUrlConverter; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; /** * m3u8直播参数设置 */ -public class M3U8LiveDelegate extends BaseOption { +public class M3U8LiveOption extends M3U8Option { - M3U8LiveDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_LIVE); + private ILiveTsUrlConverter liveTsUrlConverter; + private long liveUpdateInterval; + + public M3U8LiveOption() { + super(); } /** * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 * - * @param converter {@link ILiveTsUrlConverter} + * @param liveTsUrlConverter {@link ILiveTsUrlConverter} */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8LiveDelegate setLiveTsUrlConvert(ILiveTsUrlConverter converter) { - CheckUtil.checkMemberClass(converter.getClass()); - ((DTaskWrapper) getTaskWrapper()).getM3U8Params() - .setObjs(IOptionConstant.liveTsUrlConverter, converter); + public M3U8LiveOption setLiveTsUrlConvert(ILiveTsUrlConverter liveTsUrlConverter) { + CheckUtil.checkMemberClass(liveTsUrlConverter.getClass()); + this.liveTsUrlConverter = liveTsUrlConverter; return this; } /** * 设置直播的m3u8文件更新间隔,默认10000微秒。 * - * @param interval 更新间隔,单位微秒 + * @param liveUpdateInterval 更新间隔,单位微秒 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8LiveDelegate setM3U8FileUpdateInterval(long interval) { - if (interval <= 1) { + public M3U8LiveOption setM3U8FileUpdateInterval(long liveUpdateInterval) { + if (liveUpdateInterval <= 1) { ALog.e(TAG, "间隔时间错误"); return this; } - ((DTaskWrapper) getTaskWrapper()).getM3U8Params() - .setParams(IOptionConstant.liveUpdateInterval, interval); + + this.liveUpdateInterval = liveUpdateInterval; return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java new file mode 100644 index 00000000..fcd1b8b7 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.m3u8; + +import com.arialyy.aria.core.common.BaseOption; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; +import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.ComponentUtil; + +/** + * m3u8任务设置 + */ +public class M3U8Option extends BaseOption { + + private boolean generateIndexFileTemp = false; + private boolean mergeFile = false; + private int bandWidth; + private ITsMergeHandler mergeHandler; + private IVodTsUrlConverter vodUrlConverter; + private IBandWidthUrlConverter bandWidthUrlConverter; + + M3U8Option() { + super(); + ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8); + } + + /** + * 生成m3u8索引文件 + * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 + */ + public M3U8Option generateIndexFile() { + this.generateIndexFileTemp = true; + return this; + } + + /** + * 是否合并ts文件,默认合并ts + * + * @param mergeFile {@code true}合并所有ts文件为一个 + */ + public M3U8Option merge(boolean mergeFile) { + this.mergeFile = mergeFile; + return this; + } + + /** + * 如果你希望使用自行处理ts文件的合并,可以使用{@link ITsMergeHandler}处理ts文件的合并 + * 需要注意的是:只有{@link #merge(boolean)}设置合并ts文件,该方法才会生效 + */ + public M3U8Option setMergeHandler(ITsMergeHandler mergeHandler) { + CheckUtil.checkMemberClass(mergeHandler.getClass()); + this.mergeHandler = mergeHandler; + return this; + } + + /** + * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 + * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 + * + * @param vodUrlConverter {@link IVodTsUrlConverter} + */ + public M3U8Option setTsUrlConvert(IVodTsUrlConverter vodUrlConverter) { + CheckUtil.checkMemberClass(vodUrlConverter.getClass()); + this.vodUrlConverter = vodUrlConverter; + return this; + } + + /** + * 选择需要下载的码率,默认下载的码率 + * + * @param bandWidth 指定的码率 + */ + public M3U8Option setBandWidth(int bandWidth) { + this.bandWidth = bandWidth; + return this; + } + + /** + * M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的, + * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 + * + * @param bandWidthUrlConverter {@link IBandWidthUrlConverter} + */ + public M3U8Option setBandWidthUrlConverter(IBandWidthUrlConverter bandWidthUrlConverter) { + CheckUtil.checkMemberClass(bandWidthUrlConverter.getClass()); + this.bandWidthUrlConverter = bandWidthUrlConverter; + return this; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java deleted file mode 100644 index b1fca565..00000000 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodDelegate.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.download.m3u8; - -import androidx.annotation.CheckResult; -import com.arialyy.aria.core.common.BaseOption; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.event.PeerIndexEvent; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.queue.DTaskQueue; -import com.arialyy.aria.util.ALog; - -/** - * m3u8点播文件参数设置 - */ -public class M3U8VodDelegate extends BaseOption { - private DTaskWrapper mTaskWrapper; - - M3U8VodDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - mTaskWrapper = (DTaskWrapper) getTaskWrapper(); - mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_VOD); - } - - /** - * 由于m3u8协议的特殊性质,无法有效快速获取到正确到文件长度,如果你需要显示文件中长度,你需要自行设置文件长度 - * - * @param fileSize 文件长度 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8VodDelegate setFileSize(long fileSize) { - if (fileSize <= 0) { - ALog.e(TAG, "文件长度错误"); - return this; - } - mTaskWrapper.getEntity().setFileSize(fileSize); - return this; - } - - /** - * 默认情况下,对于同一点播文件的下载,最多同时下载4个ts分片,如果你希望增加或减少同时下载的ts分片数量,可以使用该方法设置同时下载的ts分片数量 - * - * @param num 同时下载的ts分片数量 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8VodDelegate setMaxTsQueueNum(int num) { - if (num < 1) { - ALog.e(TAG, "同时下载的分片数量不能小于1"); - return this; - } - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.maxTsQueueNum, num); - return this; - } - - /** - * 启动任务时初始化索引位置 - * - * 优先下载指定索引后的切片 - * 如果指定的切片索引大于切片总数,则此操作无效 - * 如果指定的切片索引小于当前正在下载的切片索引,并且指定索引和当前索引区间内有未下载的切片,则优先下载该区间的切片;否则此操作无效 - * 如果指定索引后的切片已经全部下载完成,但是索引前有未下载的切片,间会自动下载未下载的切片 - * - * @param index 指定的切片位置 - */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public M3U8VodDelegate setPeerIndex(int index) { - if (index < 1) { - ALog.e(TAG, "切片索引不能小于1"); - return this; - } - mTaskWrapper.getM3U8Params().setParams(IOptionConstant.jumpIndex, index); - return this; - } - - /** - * 任务执行中,跳转索引位置 - * 优先下载指定索引后的切片 - * 如果指定的切片索引大于切片总数,则此操作无效 - * 如果指定的切片索引小于当前正在下载的切片索引,并且指定索引和当前索引区间内有未下载的切片,则优先下载该区间的切片;否则此操作无效 - * 如果指定索引后的切片已经全部下载完成,但是索引前有未下载的切片,间会自动下载未下载的切片 - * - * @param index 指定的切片位置 - */ - public void jumPeerIndex(int index) { - if (index < 1) { - ALog.e(TAG, "切片索引不能小于1"); - return; - } - - if (!DTaskQueue.getInstance().taskIsRunning(mTaskWrapper.getKey())) { - ALog.e(TAG, - String.format("任务【%s】没有运行,如果你希望在启动任务时初始化索引位置,请调用setPeerIndex(xxx)", - mTaskWrapper.getKey())); - return; - } - - EventMsgUtil.getDefault().post(new PeerIndexEvent(mTaskWrapper.getKey(), index)); - } -} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java new file mode 100644 index 00000000..039009e6 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.download.m3u8; + +import com.arialyy.aria.util.ALog; + +/** + * m3u8点播文件参数设置 + */ +public class M3U8VodOption extends M3U8Option { + private long fileSize; + private int maxTsQueueNum; + private int jumpIndex; + + public M3U8VodOption() { + super(); + } + + /** + * 由于m3u8协议的特殊性质,无法有效快速获取到正确到文件长度,如果你需要显示文件中长度,你需要自行设置文件长度 + * + * @param fileSize 文件长度 + */ + public M3U8VodOption setFileSize(long fileSize) { + if (fileSize <= 0) { + ALog.e(TAG, "文件长度错误"); + return this; + } + this.fileSize = fileSize; + return this; + } + + /** + * 默认情况下,对于同一点播文件的下载,最多同时下载4个ts分片,如果你希望增加或减少同时下载的ts分片数量,可以使用该方法设置同时下载的ts分片数量 + * + * @param maxTsQueueNum 同时下载的ts分片数量 + */ + public M3U8VodOption setMaxTsQueueNum(int maxTsQueueNum) { + if (maxTsQueueNum < 1) { + ALog.e(TAG, "同时下载的分片数量不能小于1"); + return this; + } + + this.maxTsQueueNum = maxTsQueueNum; + return this; + } + + /** + * 启动任务时初始化索引位置 + * + * 优先下载指定索引后的切片 + * 如果指定的切片索引大于切片总数,则此操作无效 + * 如果指定的切片索引小于当前正在下载的切片索引,并且指定索引和当前索引区间内有未下载的切片,则优先下载该区间的切片;否则此操作无效 + * 如果指定索引后的切片已经全部下载完成,但是索引前有未下载的切片,间会自动下载未下载的切片 + * + * @param jumpIndex 指定的切片位置 + */ + public M3U8VodOption setPeerIndex(int jumpIndex) { + if (jumpIndex < 1) { + ALog.e(TAG, "切片索引不能小于1"); + return this; + } + this.jumpIndex = jumpIndex; + return this; + } + + public long getFileSize() { + return fileSize; + } + + public int getJumpIndex() { + return jumpIndex; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index f0ed90dc..b622dd56 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -18,10 +18,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -34,8 +33,6 @@ public class FtpBuilderTarget extends AbsBuilderTarget { FtpBuilderTarget(String url) { mConfigHandler = new DNormalConfigHandler<>(this, -1); mConfigHandler.setUrl(url); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(url)); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); } @@ -43,8 +40,13 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpBuilderTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(mConfigHandler.getUrl())); + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index 421756f7..95ec9264 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -17,10 +17,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -30,12 +29,12 @@ import com.arialyy.aria.util.CommonUtil; */ public class FtpDirBuilderTarget extends AbsBuilderTarget { private FtpDirConfigHandler mConfigHandler; + private String url; FtpDirBuilderTarget(String url) { + this.url = url; mConfigHandler = new FtpDirConfigHandler<>(this, -1); getEntity().setGroupHash(url); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(url)); } /** @@ -75,8 +74,13 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpDirBuilderTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(url)); + getTaskWrapper().getOptionParams().setParams(option); + return this; } @Override public DownloadGroupEntity getEntity() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index baff2e01..e657475c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -17,10 +17,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -33,8 +32,6 @@ public class FtpDirNormalTarget extends AbsNormalTarget { FtpDirNormalTarget(long taskId) { mConfigHandler = new FtpDirConfigHandler<>(this, taskId); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getKey())); } @Override public boolean isRunning() { @@ -73,8 +70,13 @@ public class FtpDirNormalTarget extends AbsNormalTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpDirNormalTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); + getTaskWrapper().getOptionParams().setParams(option); + return this; } @Override public DownloadGroupEntity getEntity() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index c7e48d0d..aec82b9e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -18,10 +18,9 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -34,8 +33,6 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new DNormalConfigHandler<>(this, taskId); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); } @@ -43,8 +40,13 @@ public class FtpNormalTarget extends AbsNormalTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpNormalTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index 0bd189ca..c0e3e100 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.HttpDelegate; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; @@ -45,8 +45,12 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public GroupBuilderTarget option(HttpOption option) { + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index e70d2143..5c3be712 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -17,10 +17,10 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.HttpDelegate; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.manager.SubTaskManager; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.util.List; /** @@ -39,8 +39,12 @@ public class GroupNormalTarget extends AbsNormalTarget { * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public GroupNormalTarget option(HttpOption option) { + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index ff3a81ef..213a8886 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -18,11 +18,14 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.HttpDelegate; -import com.arialyy.aria.core.download.m3u8.M3U8Delegate; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.common.HttpOption; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; +import com.arialyy.aria.core.download.m3u8.M3U8VodOption; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CheckUtil; @@ -36,16 +39,37 @@ public class HttpBuilderTarget extends AbsBuilderTarget { mConfigHandler.setUrl(url); } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public M3U8Delegate asM3U8() { - return new M3U8Delegate<>(this, getTaskWrapper()); + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public HttpBuilderTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { + if (m3U8VodOption == null){ + throw new NullPointerException("m3u8任务设置为空"); + } + getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_VOD); + getTaskWrapper().getEntity().setFileSize(m3U8VodOption.getFileSize()); + ((DTaskWrapper) getTaskWrapper()).getM3U8Params().setParams(m3U8VodOption); + return this; + } + + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public HttpBuilderTarget m3u8LiveOption(M3U8LiveOption m3U8LiveOption) { + if (m3U8LiveOption == null){ + throw new NullPointerException("m3u8任务设置为空"); + } + getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_LIVE); + ((DTaskWrapper) getTaskWrapper()).getM3U8Params().setParams(m3U8LiveOption); + return this; } /** * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public HttpBuilderTarget option(HttpOption option) { + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java index aed1f7ba..d17e9b3a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java @@ -17,10 +17,13 @@ package com.arialyy.aria.core.download.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.HttpDelegate; +import com.arialyy.aria.core.common.HttpOption; +import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.m3u8.M3U8Delegate; +import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; +import com.arialyy.aria.core.download.m3u8.M3U8VodOption; +import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** * Created by lyy on 2016/12/5. @@ -35,16 +38,41 @@ public class HttpNormalTarget extends AbsNormalTarget { } @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public M3U8Delegate asM3U8() { - return new M3U8Delegate<>(this, getTaskWrapper()); + public M3U8NormalTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { + if (m3U8VodOption == null) { + throw new NullPointerException("m3u8任务设置为空"); + } + getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_VOD); + getTaskWrapper().getEntity().setFileSize(m3U8VodOption.getFileSize()); + ((DTaskWrapper) getTaskWrapper()).getM3U8Params().setParams(m3U8VodOption); + return new M3U8NormalTarget((DTaskWrapper) getTaskWrapper()); + } + + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public M3U8NormalTarget m3u8VodOption() { + return new M3U8NormalTarget((DTaskWrapper) getTaskWrapper()); + } + + @CheckResult(suggest = Suggest.TASK_CONTROLLER) + public HttpNormalTarget m3u8LiveOption(M3U8LiveOption m3U8LiveOption) { + if (m3U8LiveOption == null) { + throw new NullPointerException("m3u8任务设置为空"); + } + getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_LIVE); + ((DTaskWrapper) getTaskWrapper()).getM3U8Params().setParams(m3U8LiveOption); + return this; } /** * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public HttpNormalTarget option(HttpOption option) { + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java new file mode 100644 index 00000000..2b530b85 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java @@ -0,0 +1,56 @@ + +package com.arialyy.aria.core.download.target; +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.event.EventMsgUtil; +import com.arialyy.aria.core.event.PeerIndexEvent; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.util.ALog; + +public class M3U8NormalTarget extends AbsNormalTarget { + + M3U8NormalTarget(DTaskWrapper wrapper) { + setTaskWrapper(wrapper); + } + + /** + * 任务执行中,跳转索引位置 + * 优先下载指定索引后的切片 + * 如果指定的切片索引大于切片总数,则此操作无效 + * 如果指定的切片索引小于当前正在下载的切片索引,并且指定索引和当前索引区间内有未下载的切片,则优先下载该区间的切片;否则此操作无效 + * 如果指定索引后的切片已经全部下载完成,但是索引前有未下载的切片,间会自动下载未下载的切片 + * + * @param index 指定的切片位置 + */ + public void jumPeerIndex(int index) { + if (index < 1) { + ALog.e(TAG, "切片索引不能小于1"); + return; + } + + if (!DTaskQueue.getInstance().taskIsRunning(getTaskWrapper().getKey())) { + ALog.e(TAG, + String.format("任务【%s】没有运行,如果你希望在启动任务时初始化索引位置,请调用setPeerIndex(xxx)", + getTaskWrapper().getKey())); + return; + } + + EventMsgUtil.getDefault().post(new PeerIndexEvent(getTaskWrapper().getKey(), index)); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java index bd2918fe..4b492cb0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -34,14 +34,14 @@ public class TcpBuilderTarget extends AbsBuilderTarget { mConfigHandler = new DNormalConfigHandler<>(this, -1); getTaskWrapper().setRequestType(ITaskWrapper.D_TCP); } - - /** - * 设置tcp相应信息 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public TcpDelegate option() { - return new TcpDelegate<>(this, getTaskWrapper()); - } + // + ///** + // * 设置tcp相应信息 + // */ + //@CheckResult(suggest = Suggest.TASK_CONTROLLER) + //public TcpBuilderTarget option(T) { + // return new TcpDelegate<>(this, getTaskWrapper()); + //} /** * 设置文件存储路径,如果需要修改新的文件名,修改路径便可。 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java index f77632ef..14e689ca 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/tcp/TcpDelegate.java @@ -16,11 +16,7 @@ package com.arialyy.aria.core.download.tcp; import android.text.TextUtils; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.BaseOption; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.inf.AbsTarget; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import java.nio.charset.Charset; @@ -28,26 +24,26 @@ import java.nio.charset.Charset; * @Author aria * @Date 2019-09-06 */ -public class TcpDelegate extends BaseOption { +public class TcpDelegate extends BaseOption { - private TcpTaskConfig mTcpConfig; + private String params; + private String heartbeatInfo; + private long heartbeat; + private String charset; - public TcpDelegate(TARGET target, AbsTaskWrapper wrapper) { - super(target, wrapper); - - mTcpConfig = (TcpTaskConfig) wrapper.getTaskOption(); + public TcpDelegate() { + super(); } /** * 上传给tcp服务的初始数据,一般是文件名、文件路径等信息 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public TcpDelegate setParam(String params) { + public TcpDelegate setParam(String params) { if (TextUtils.isEmpty(params)) { ALog.w(TAG, "tcp传输的数据不能为空"); return this; } - mTcpConfig.setParams(params); + this.params = params; return this; } @@ -56,41 +52,39 @@ public class TcpDelegate extends BaseOption { * * @param heartbeatInfo 心跳包数据 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public TcpDelegate setHeartbeatInfo(String heartbeatInfo) { + public TcpDelegate setHeartbeatInfo(String heartbeatInfo) { if (TextUtils.isEmpty(heartbeatInfo)) { ALog.w(TAG, "心跳包传输的数据不能为空"); return this; } - mTcpConfig.setHeartbeat(heartbeatInfo); + this.heartbeatInfo = heartbeatInfo; return this; } /** * 心跳间隔,默认1s * - * @param interval 单位毫秒 + * @param heartbeat 单位毫秒 */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) - public TcpDelegate setHeartbeatInterval(long interval) { - if (interval <= 0) { + public TcpDelegate setHeartbeatInterval(long heartbeat) { + if (heartbeat <= 0) { ALog.w(TAG, "心跳间隔不能小于1毫秒"); return this; } - mTcpConfig.setHeartbeatInterval(interval); + this.heartbeat = heartbeat; return this; } /** * 数据传输编码,默认"utf-8" */ - public TcpDelegate setCharset(String charset) { + public TcpDelegate setCharset(String charset) { if (!Charset.isSupported(charset)) { ALog.w(TAG, "不支持的编码"); return this; } - mTcpConfig.setCharset(charset); + this.charset = charset; return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index e9c1119e..1f7d3f91 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -17,12 +17,13 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; -import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.FtpDelegate; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; /** * Created by Aria.Lao on 2017/7/27. @@ -30,6 +31,7 @@ import com.arialyy.aria.util.CheckUtil; */ public class FtpBuilderTarget extends AbsBuilderTarget { private UNormalConfigHandler mConfigHandler; + private String url; FtpBuilderTarget(String filePath) { mConfigHandler = new UNormalConfigHandler<>(this, -1); @@ -44,6 +46,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget setUploadUrl(String tempUrl) { + url = tempUrl; mConfigHandler.setTempUrl(tempUrl); return this; } @@ -61,7 +64,12 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpBuilderTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(url)); + getTaskWrapper().getOptionParams().setParams(option); + return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index 994b3004..15a5e88e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -17,11 +17,10 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.FtpDelegate; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.CommonUtil; /** @@ -33,9 +32,6 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new UNormalConfigHandler<>(this, taskId); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(getEntity().getUrl())); - getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); } @@ -43,8 +39,13 @@ public class FtpNormalTarget extends AbsNormalTarget { * 设置登陆、字符串编码、ftps等参数 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpDelegate option() { - return new FtpDelegate<>(this, getTaskWrapper()); + public FtpNormalTarget option(FtpOption option) { + if (option == null) { + throw new NullPointerException("ftp 任务配置为空"); + } + option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + getTaskWrapper().getOptionParams().setParams(option); + return this; } @Override public UploadEntity getEntity() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 89ede1dd..b8b7ea5f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -17,8 +17,8 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -52,7 +52,12 @@ public class HttpBuilderTarget extends AbsBuilderTarget { * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public HttpBuilderTarget option(HttpOption option) { + + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index 8edf5eb5..2b7a3d5d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -17,8 +17,8 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.common.HttpDelegate; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -49,8 +49,12 @@ public class HttpNormalTarget extends AbsNormalTarget { * 设置http请求参数,header等信息 */ @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpDelegate option() { - return new HttpDelegate<>(this, getTaskWrapper()); + public HttpNormalTarget option(HttpOption option) { + if (option == null) { + throw new NullPointerException("任务配置为空"); + } + getTaskWrapper().getOptionParams().setParams(option); + return this; } @Override public boolean isRunning() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java index 251e628c..b24c172d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java @@ -86,8 +86,6 @@ class UNormalConfigHandler implements IConfigHandler { void setTempUrl(String tempUrl) { getTaskWrapper().setTempUrl(tempUrl); - getTaskWrapper().getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, CommonUtil.getFtpUrlInfo(tempUrl)); } private UTaskWrapper getTaskWrapper() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java index baa2fa5d..e82ec1e7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java @@ -15,8 +15,11 @@ */ package com.arialyy.aria.core; +import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.util.CommonUtil; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; @@ -38,6 +41,34 @@ public class TaskOptionParams { */ private Map handler = new HashMap<>(); + /** + * 设置任务参数 + * + * @param option 任务配置 + */ + public void setParams(BaseOption option) { + Field[] fields = CommonUtil.getFields(option.getClass()); + + for (Field field : fields) { + field.setAccessible(true); + try { + if (field.getType() == IEventHandler.class) { + Object eventHandler = field.get(option); + if (eventHandler != null) { + setObjs(field.getName(), (IEventHandler) eventHandler); + } + } else { + Object params = field.get(option); + if (params != null) { + setParams(field.getName(), params); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + /** * 设置普通参数 * @@ -60,11 +91,11 @@ public class TaskOptionParams { return params; } - public Object getParam(String key){ + public Object getParam(String key) { return params.get(key); } - public IEventHandler getHandler(String key){ + public IEventHandler getHandler(String key) { return handler.get(key); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/BaseOption.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/BaseOption.java new file mode 100644 index 00000000..68955305 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/BaseOption.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +import com.arialyy.aria.util.CommonUtil; + +public abstract class BaseOption { + protected final String TAG; + + public BaseOption() { + TAG = CommonUtil.getClassName(getClass()); + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index d2713dc6..cbf97a57 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -21,17 +21,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Environment; -import android.os.Handler; import android.text.TextUtils; import android.util.Base64; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.FtpUrlEntity; -import com.arialyy.aria.core.TaskOptionParams; -import com.arialyy.aria.core.inf.IEventHandler; -import com.arialyy.aria.core.inf.ITaskOption; -import com.arialyy.aria.core.listener.IEventListener; -import com.arialyy.aria.core.task.ITask; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import dalvik.system.DexFile; import java.io.File; import java.io.FileFilter; @@ -41,7 +34,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -69,7 +61,6 @@ public class CommonUtil { public static final String SERVER_CHARSET = "ISO-8859-1"; private static long lastClickTime; - /** * 检查sql的expression是否合法 * diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 5e9f8acd..c33235a0 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -23,8 +23,7 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.ProtocolType; -import com.arialyy.aria.core.common.controller.ControllerType; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.DownloadTask; @@ -89,16 +88,14 @@ public class FtpDownloadActivity extends BaseActivity { } private void startD() { + HttpOption option = new HttpOption(); + option.addHeader("1", "@"); mTaskId = Aria.download(SingleTaskActivity.this) .load(mUrl) .useServerFileName(true) .setFilePath(mFilePath, true) .setFileLenAdapter(new FileLenAdapter()) - .option() - .addHeader("1", "@") - .controller(ControllerType.CREATE_CONTROLLER) + .option(option) .create(); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index b8077dbb..8f8c0c1c 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -20,7 +20,7 @@ import android.os.Environment; import android.view.View; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.common.controller.ControllerType; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.DownloadGroupTask; @@ -78,9 +78,7 @@ public class FTPDirDownloadActivity extends BaseActivity switch (view.getId()) { case R.id.start: startD(); - //if (!AppUtil.chekEntityValid(mEntity)) { - // startD(); - // break; - //} - //if (Aria.download(this).load(mEntity.getId()).isRunning()) { - // Aria.download(this).load(mEntity.getId()).stop(); - //} else { - // Aria.download(this).load(mEntity.getId()) - // .asM3U8() - // .asLive() - // .setLiveTsUrlConvert(new ILiveTsUrlConverter() { - // @Override public String convert(String m3u8Url, String tsUrl) { - // int index = m3u8Url.lastIndexOf("/"); - // String parentUrl = m3u8Url.substring(0, index + 1); - // return parentUrl + tsUrl; - // } - // }) - // .controller(ControllerType.TASK_CONTROLLER) - // .resume(); - //} - //break; + if (!AppUtil.chekEntityValid(mEntity)) { + startD(); + break; + } + if (Aria.download(this).load(mEntity.getId()).isRunning()) { + Aria.download(this).load(mEntity.getId()).stop(); + } else { + Aria.download(this).load(mEntity.getId()) + .m3u8LiveOption(getLiveoption()) + .resume(); + } + break; case R.id.cancel: if (AppUtil.chekEntityValid(mEntity)) { Aria.download(this).load(mEntity.getId()).cancel(true); @@ -247,31 +239,18 @@ public class M3U8LiveDLoadActivity extends BaseActivity .load(mUrl) .useServerFileName(true) .setFilePath(mFilePath, true) - .asM3U8() - //.setBandWidthUrlConverter(new IBandWidthUrlConverter() { - // @Override public String convert(String bandWidthUrl) { - // int peerIndex = mUrl.lastIndexOf("/"); - // return mUrl.substring(0, peerIndex + 1) + bandWidthUrl; - // } - //}) - .asLive() - .setLiveTsUrlConvert(new LiveTsUrlConverter()) - .controller(ControllerType.CREATE_CONTROLLER) - //.setLiveTsUrlConvert(new IVodTsUrlConverter() { - // @Override public List convert(String m3u8Url, List tsUrls) { - // int peerIndex = m3u8Url.lastIndexOf("/"); - // String parentUrl = m3u8Url.substring(0, peerIndex + 1); - // List newUrls = new ArrayList<>(); - // for (String url : tsUrls) { - // newUrls.add(parentUrl + url); - // } - // - // return newUrls; - // } - //}) + .m3u8LiveOption(getLiveoption()) .create(); } + private M3U8LiveOption getLiveoption() { + M3U8LiveOption option = new M3U8LiveOption(); + option + .setLiveTsUrlConvert(new LiveTsUrlConverter()); + //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); + return option; + } + @Override protected void dataCallback(int result, Object data) { super.dataCallback(result, data); if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) { @@ -288,4 +267,17 @@ public class M3U8LiveDLoadActivity extends BaseActivity return parentUrl + tsUrl; } } + + static class BandWidthUrlConverter implements IBandWidthUrlConverter { + String url; + + BandWidthUrlConverter(String url) { + this.url = url; + } + + @Override public String convert(String bandWidthUrl) { + int peerIndex = url.lastIndexOf("/"); + return url.substring(0, peerIndex + 1) + bandWidthUrl; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index a42d1a1a..23c93312 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -31,10 +31,11 @@ import com.arialyy.annotations.Download; import com.arialyy.annotations.M3U8; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.controller.BuilderController; -import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.download.m3u8.M3U8VodOption; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.core.task.DownloadTask; @@ -122,7 +123,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @Subscribe(threadMode = ThreadMode.MAIN) public void jumpIndex(PeerIndex index) { - Aria.download(this).load(mUrl).asM3U8().asVod().jumPeerIndex(index.index); + Aria.download(this).load(mTaskId).m3u8VodOption().jumPeerIndex(index.index); } @Override @@ -280,27 +281,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { } else { Aria.download(this) .load(mTaskId) - .asM3U8() - //.setBandWidthUrlConverter(new IBandWidthUrlConverter() { - // @Override public String convert(String bandWidthUrl) { - // int index = mUrl.lastIndexOf("/"); - // return mUrl.substring(0, index + 1) + bandWidthUrl; - // } - //}) - .setTsUrlConvert(new IVodTsUrlConverter() { - @Override public List convert(String m3u8Url, List tsUrls) { - - Uri uri = Uri.parse(m3u8Url); - String parentUrl = "http://" + uri.getHost(); - List newUrls = new ArrayList<>(); - for (String url : tsUrls) { - newUrls.add(parentUrl + url); - } - - return newUrls; - } - }) - .controller(ControllerType.TASK_CONTROLLER) + .m3u8VodOption(getM3U8Option()) .resume(); } break; @@ -316,36 +297,20 @@ public class M3U8VodDLoadActivity extends BaseActivity { .load(mUrl) .useServerFileName(true) .setFilePath(mFilePath, true) - .asM3U8() - //.setBandWidthUrlConverter(new IBandWidthUrlConverter() { - // @Override public String convert(String bandWidthUrl) { - // int index = mUrl.lastIndexOf("/"); - // return mUrl.substring(0, index + 1) + bandWidthUrl; - // } - //}) - .setTsUrlConvert(new IVodTsUrlConverter() { - @Override public List convert(String m3u8Url, List tsUrls) { - Uri uri = Uri.parse(m3u8Url); - String parentUrl = uri.getScheme() + "://" + uri.getHost(); - List newUrls = new ArrayList<>(); - for (String url : tsUrls) { - newUrls.add(parentUrl + url); - } - - return newUrls; - } - }) - .setMergeHandler(new ITsMergeHandler() { - public boolean merge(@Nullable M3U8Entity m3U8Entity, List tsPath) { - ALog.d(TAG, "合并TS...."); - return false; - } - }) - .generateIndexFile() - .controller(ControllerType.CREATE_CONTROLLER) + .m3u8VodOption(getM3U8Option()) .create(); } + private M3U8VodOption getM3U8Option() { + M3U8VodOption option = new M3U8VodOption(); + option + .generateIndexFile() + .setTsUrlConvert(new VodTsUrlConverter()) + .setMergeHandler(new TsMergeHandler()); + //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); + return option; + } + private Class c = BuilderController.class; @Override protected void dataCallback(int result, Object data) { @@ -356,4 +321,38 @@ public class M3U8VodDLoadActivity extends BaseActivity { mModule.updateFilePath(this, String.valueOf(data)); } } + + static class VodTsUrlConverter implements IVodTsUrlConverter { + @Override public List convert(String m3u8Url, List tsUrls) { + Uri uri = Uri.parse(m3u8Url); + String parentUrl = "http://" + uri.getHost(); + List newUrls = new ArrayList<>(); + for (String url : tsUrls) { + newUrls.add(parentUrl + url); + } + + return newUrls; + } + } + + static class TsMergeHandler implements ITsMergeHandler { + public boolean merge(@Nullable M3U8Entity m3U8Entity, List tsPath) { + ALog.d("TsMergeHandler", "合并TS...."); + return false; + } + } + + static class BandWidthUrlConverter implements IBandWidthUrlConverter { + + private String url; + + BandWidthUrlConverter(String url) { + this.url = url; + } + + @Override public String convert(String bandWidthUrl) { + int index = url.lastIndexOf("/"); + return url.substring(0, index + 1) + bandWidthUrl; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java index 988df6f2..07bf5126 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java @@ -25,10 +25,8 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.common.controller.ControllerType; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.processor.FtpInterceptHandler; -import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.util.ALog; @@ -42,7 +40,6 @@ import com.arialyy.simple.databinding.ActivityFtpUploadBinding; import com.arialyy.simple.util.AppUtil; import java.io.File; import java.io.IOException; -import java.util.List; /** * Created by lyy on 2017/7/28. Ftp 文件上传demo @@ -118,9 +115,7 @@ public class FtpUploadActivity extends BaseActivity { mTaskId = Aria.upload(this) .loadFtp(mFilePath) .setUploadUrl(mUrl) - .option() - .login(user, pwd) - .controller(ControllerType.CREATE_CONTROLLER) + .option(getOption()) .create(); getBinding().setStateStr(getString(R.string.stop)); break; @@ -131,9 +126,7 @@ public class FtpUploadActivity extends BaseActivity { } else { Aria.upload(this) .loadFtp(mTaskId) - .option() - .login(user, pwd) - .controller(ControllerType.TASK_CONTROLLER) + .option(getOption()) .resume(); getBinding().setStateStr(getString(R.string.stop)); } @@ -147,6 +140,12 @@ public class FtpUploadActivity extends BaseActivity { } } + private FtpOption getOption() { + FtpOption option = new FtpOption(); + option.login(user, pwd); + return option; + } + @Upload.onWait void onWait(UploadTask task) { Log.d(TAG, task.getTaskName() + "_wait"); } diff --git a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java index a4bd69bb..4f6082a9 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/HttpUploadActivity.java @@ -21,8 +21,8 @@ import android.os.Environment; import android.view.View; import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.frame.util.FileUtil; @@ -76,15 +76,15 @@ public class HttpUploadActivity extends BaseActivity { } void upload() { + HttpOption option = new HttpOption(); + option.setRequestType(RequestEnum.POST) + .setParam("params", "bbbbbbbb"); Aria.upload(HttpUploadActivity.this).load(FILE_PATH) //.setUploadUrl("http://lib-test.xzxyun.com:8042/Api/upload?data={\"type\":\"1\",\"fileType\":\".apk\"}") .setUploadUrl("http://9.9.9.205:5000/upload/") //.setTempUrl("http://192.168.1.6:8080/upload/sign_file/").setAttachment("file") //.addHeader("iplanetdirectorypro", "11a09102fb934ad0bc206f9c611d7933") - .option() - .setRequestType(RequestEnum.POST) - .setParam("params", "bbbbbbbb") - .controller(ControllerType.CREATE_CONTROLLER) + .option(option) .create(); } diff --git a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java index 9bd6459f..4ce216c8 100644 --- a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java +++ b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java @@ -20,7 +20,7 @@ import android.os.Environment; import android.util.Log; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; -import com.arialyy.aria.core.common.controller.ControllerType; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.CommonUtil; @@ -108,18 +108,21 @@ public class AnyRunnModule { Aria.download(this) .loadFtp(url) .setFilePath(Environment.getExternalStorageDirectory().getPath() + "/Download/") - .option() - .asFtps() - .setStorePath("/mnt/sdcard/Download/server.crt") - .setAlias("www.laoyuyu.me") - .setStorePass("123456") - .controller(ControllerType.CREATE_CONTROLLER) + .option(getFtpOption()) .create(); } else { Aria.download(this).load(mEntity.getId()).resume(); } } + private FtpOption getFtpOption() { + FtpOption option = new FtpOption(); + option.setStorePath("/mnt/sdcard/Download/server.crt") + .setAlias("www.laoyuyu.me") + .setStorePass("123456"); + return option; + } + public void stop(String url) { if (AppUtil.chekEntityValid(mEntity)) { Aria.download(this).load(mEntity.getId()).stop(); From fb0b95bd075f40c274e6822ba777c61cba28d0d8 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Fri, 18 Oct 2019 20:04:45 +0800 Subject: [PATCH 008/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgradle=205.0=20apt=20?= =?UTF-8?q?=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppFrame/build.gradle | 4 +- Aria/bintray-release.gradle | 7 +- Aria/build.gradle | 12 +- .../arialyy/aria/core/common/FtpOption.java | 41 +- .../arialyy/aria/core/common/HttpOption.java | 29 + .../aria/core/download/DownloadReceiver.java | 3 +- .../download/target/FtpBuilderTarget.java | 2 +- .../download/target/FtpDirBuilderTarget.java | 2 +- .../download/target/FtpDirNormalTarget.java | 2 +- .../core/download/target/FtpNormalTarget.java | 2 +- .../download/target/GroupBuilderTarget.java | 17 - .../download/target/HttpBuilderTarget.java | 25 - .../aria/core/upload/UploadReceiver.java | 2 +- .../core/upload/target/FtpBuilderTarget.java | 14 +- .../core/upload/target/FtpNormalTarget.java | 2 +- .../upload/target/UNormalConfigHandler.java | 9 - AriaAnnotations/bintray-release.gradle | 7 +- AriaCompiler/bintray-release.gradle | 7 +- AriaCompiler/build.gradle | 5 +- FtpComponent/bintray-release.gradle | 13 + FtpComponent/build.gradle | 5 +- .../commons/net/DatagramSocketClient.java | 287 ++ .../commons/net/DatagramSocketFactory.java | 67 + .../net/DefaultDatagramSocketFactory.java | 72 + .../commons/net/DefaultSocketFactory.java | 204 + .../net/MalformedServerReplyException.java | 52 + .../commons/net/PrintCommandListener.java | 190 + .../commons/net/ProtocolCommandEvent.java | 139 + .../commons/net/ProtocolCommandListener.java | 58 + .../commons/net/ProtocolCommandSupport.java | 123 + .../aria/apache/commons/net/SocketClient.java | 869 ++++ .../apache/commons/net/ftp/Configurable.java | 34 + .../java/aria/apache/commons/net/ftp/FTP.java | 1777 ++++++++ .../apache/commons/net/ftp/FTPClient.java | 3787 +++++++++++++++++ .../commons/net/ftp/FTPClientConfig.java | 713 ++++ .../aria/apache/commons/net/ftp/FTPCmd.java | 75 + .../apache/commons/net/ftp/FTPCommand.java | 170 + .../net/ftp/FTPConnectionClosedException.java | 52 + .../aria/apache/commons/net/ftp/FTPFile.java | 493 +++ .../commons/net/ftp/FTPFileEntryParser.java | 129 + .../net/ftp/FTPFileEntryParserImpl.java | 72 + .../apache/commons/net/ftp/FTPFileFilter.java | 34 + .../commons/net/ftp/FTPFileFilters.java | 54 + .../apache/commons/net/ftp/FTPHTTPClient.java | 194 + .../commons/net/ftp/FTPListParseEngine.java | 311 ++ .../aria/apache/commons/net/ftp/FTPReply.java | 193 + .../apache/commons/net/ftp/FTPSClient.java | 931 ++++ .../apache/commons/net/ftp/FTPSCommand.java | 52 + .../net/ftp/FTPSServerSocketFactory.java | 72 + .../commons/net/ftp/FTPSSocketFactory.java | 116 + .../commons/net/ftp/FTPSTrustManager.java | 54 + .../net/ftp/OnFtpInputStreamListener.java | 33 + .../apache/commons/net/ftp/package-info.java | 21 + .../ftp/parser/CompositeFileEntryParser.java | 59 + .../ConfigurableFTPFileEntryParserImpl.java | 123 + .../DefaultFTPFileEntryParserFactory.java | 254 ++ .../parser/EnterpriseUnixFTPEntryParser.java | 159 + .../ftp/parser/FTPFileEntryParserFactory.java | 63 + .../net/ftp/parser/FTPTimestampParser.java | 53 + .../ftp/parser/FTPTimestampParserImpl.java | 403 ++ .../net/ftp/parser/MLSxEntryParser.java | 260 ++ .../net/ftp/parser/MVSFTPEntryParser.java | 537 +++ .../ftp/parser/MacOsPeterFTPEntryParser.java | 245 ++ .../net/ftp/parser/NTFTPEntryParser.java | 142 + .../net/ftp/parser/NetwareFTPEntryParser.java | 175 + .../net/ftp/parser/OS2FTPEntryParser.java | 127 + .../net/ftp/parser/OS400FTPEntryParser.java | 399 ++ .../parser/ParserInitializationException.java | 60 + .../parser/RegexFTPFileEntryParserImpl.java | 199 + .../net/ftp/parser/UnixFTPEntryParser.java | 335 ++ .../net/ftp/parser/VMSFTPEntryParser.java | 251 ++ .../parser/VMSVersioningFTPEntryParser.java | 153 + .../commons/net/ftp/parser/package-info.java | 21 + .../apache/commons/net/io/CRLFLineReader.java | 75 + .../commons/net/io/CopyStreamAdapter.java | 111 + .../commons/net/io/CopyStreamEvent.java | 113 + .../commons/net/io/CopyStreamException.java | 69 + .../commons/net/io/CopyStreamListener.java | 68 + .../net/io/DotTerminatedMessageReader.java | 238 ++ .../net/io/DotTerminatedMessageWriter.java | 189 + .../net/io/FromNetASCIIInputStream.java | 196 + .../net/io/FromNetASCIIOutputStream.java | 150 + .../commons/net/io/SocketInputStream.java | 64 + .../commons/net/io/SocketOutputStream.java | 80 + .../commons/net/io/ToNetASCIIInputStream.java | 165 + .../net/io/ToNetASCIIOutputStream.java | 105 + .../java/aria/apache/commons/net/io/Util.java | 351 ++ .../apache/commons/net/io/package-info.java | 21 + .../aria/apache/commons/net/util/Base64.java | 1056 +++++ .../apache/commons/net/util/Charsets.java | 53 + .../commons/net/util/KeyManagerUtils.java | 236 + .../apache/commons/net/util/ListenerList.java | 59 + .../commons/net/util/SSLContextUtils.java | 77 + .../commons/net/util/SSLSocketUtils.java | 68 + .../apache/commons/net/util/SubnetUtils.java | 395 ++ .../commons/net/util/TrustManagerUtils.java | 113 + .../apache/commons/net/util/package-info.java | 21 + HttpComponent/bintray-release.gradle | 13 + HttpComponent/build.gradle | 2 + M3U8Component/bintray-release.gradle | 13 + M3U8Component/build.gradle | 2 + NOTICE | 10 + PublicComponent/bintray-release.gradle | 13 + PublicComponent/build.gradle | 2 + .../com/arialyy/aria/core/AriaConfig.java | 26 +- .../arialyy/aria/core/TaskOptionParams.java | 1 + .../arialyy/aria/core/common/ErrorCode.java | 34 + .../aria/core/common/RecordHandler.java | 13 - .../core/processor/IFtpUploadInterceptor.java | 20 - .../aria/core/wrapper/AbsTaskWrapper.java | 12 + .../java/com/arialyy/aria/util/CheckUtil.java | 49 +- app/build.gradle | 19 +- app/src/main/assets/aria_config.xml | 24 +- app/src/main/assets/aria_config_1.xml | 161 + .../arialyy/simple/base/BaseApplication.java | 14 +- .../core/download/KotlinDownloadActivity.kt | 1 - .../core/download/SingleTaskActivity.java | 7 +- .../download/group/DownloadGroupActivity.java | 33 +- .../download/m3u8/M3U8LiveDLoadActivity.java | 1 - .../download/m3u8/M3U8VodDLoadActivity.java | 1 - build.gradle | 14 +- gradle.properties | 7 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 123 files changed, 19970 insertions(+), 254 deletions(-) create mode 100644 FtpComponent/bintray-release.gradle create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketClient.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/PrintCommandListener.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/SocketClient.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/Configurable.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTP.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPReply.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/package-info.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamException.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/SocketInputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/Util.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/io/package-info.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/Base64.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/Charsets.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/ListenerList.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/SubnetUtils.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java create mode 100644 FtpComponent/src/main/java/aria/apache/commons/net/util/package-info.java create mode 100644 HttpComponent/bintray-release.gradle create mode 100644 M3U8Component/bintray-release.gradle create mode 100644 NOTICE create mode 100644 PublicComponent/bintray-release.gradle create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java create mode 100644 app/src/main/assets/aria_config_1.xml diff --git a/AppFrame/build.gradle b/AppFrame/build.gradle index bf09497a..2dc8b08a 100644 --- a/AppFrame/build.gradle +++ b/AppFrame/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion rootProject.ext.compileSdkVersion @@ -43,6 +45,6 @@ dependencies { api 'androidx.lifecycle:lifecycle-runtime:2.0.0' api 'androidx.lifecycle:lifecycle-extensions:2.0.0' - annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.0.0' + kapt 'androidx.lifecycle:lifecycle-compiler:2.0.0' } diff --git a/Aria/bintray-release.gradle b/Aria/bintray-release.gradle index 0e8b78df..4a861c11 100644 --- a/Aria/bintray-release.gradle +++ b/Aria/bintray-release.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.novoda.bintray-release' publish { - artifactId = 'aria-core' +// artifactId = 'aria-core' +// uploadName = 'AriaApi' + artifactId = 'core' + uploadName = 'Core' userOrg = rootProject.ext.userOrg groupId = rootProject.ext.groupId - // uploadName = rootProject.uploadName - uploadName = 'AriaApi' publishVersion = rootProject.ext.publishVersion desc = rootProject.ext.desc website = rootProject.ext.website diff --git a/Aria/build.gradle b/Aria/build.gradle index 0c8ad2ce..14de1143 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -20,19 +20,13 @@ android { abortOnError false } } - dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation 'junit:junit:4.12' implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" api project(':AriaAnnotations') api project(path: ':PublicComponent') - -// api 'com.arialyy.aria:aria-ftp-plug:1.0.4' - implementation project(path: ':HttpComponent') // 打包时用这个 - - // compile project(':AriaFtpPlug') -// implementation project(path: ':AriaFtpComponent') - + api project(path: ':HttpComponent') } -apply from: 'bintray-release.gradle' + +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java index ff5c1b52..6c737d39 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java @@ -18,7 +18,9 @@ package com.arialyy.aria.core.common; import android.text.TextUtils; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.ProtocolType; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; /** * Created by laoyuyu on 2018/3/9. @@ -27,9 +29,10 @@ public class FtpOption extends BaseOption { private String charSet, userName, password, account; private boolean isNeedLogin = false; - private FtpUrlEntity ftpUrlEntity; + private FtpUrlEntity urlEntity; private String protocol, keyAlias, storePass, storePath; private boolean isImplicit = true; + private IFtpUploadInterceptor uploadInterceptor; public FtpOption() { super(); @@ -128,19 +131,31 @@ public class FtpOption extends BaseOption { return this; } - public void setFtpUrlEntity(FtpUrlEntity ftpUrlEntity) { - this.ftpUrlEntity = ftpUrlEntity; - ftpUrlEntity.needLogin = isNeedLogin; - ftpUrlEntity.user = userName; - ftpUrlEntity.password = password; - ftpUrlEntity.account = account; + /** + * FTP文件上传拦截器,如果远端已有同名文件,可使用该拦截器控制覆盖文件或修改该文件上传到服务器端端文件名 + */ + public FtpOption setUploadInterceptor(IFtpUploadInterceptor uploadInterceptor) { + if (uploadInterceptor == null) { + throw new NullPointerException("ftp拦截器为空"); + } + CheckUtil.checkMemberClass(uploadInterceptor.getClass()); + this.uploadInterceptor = uploadInterceptor; + return this; + } + + public void setUrlEntity(FtpUrlEntity urlEntity) { + this.urlEntity = urlEntity; + urlEntity.needLogin = isNeedLogin; + urlEntity.user = userName; + urlEntity.password = password; + urlEntity.account = account; if (!TextUtils.isEmpty(storePath)) { - ftpUrlEntity.isFtps = true; - ftpUrlEntity.protocol = protocol; - ftpUrlEntity.keyAlias = keyAlias; - ftpUrlEntity.storePass = storePass; - ftpUrlEntity.storePath = storePath; - ftpUrlEntity.isImplicit = isImplicit; + urlEntity.isFtps = true; + urlEntity.protocol = protocol; + urlEntity.keyAlias = keyAlias; + urlEntity.storePass = storePass; + urlEntity.storePath = storePath; + urlEntity.isImplicit = isImplicit; } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java index 0eb46613..e52c3866 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java @@ -18,8 +18,12 @@ package com.arialyy.aria.core.common; import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; +import com.arialyy.aria.core.download.target.HttpBuilderTarget; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; import java.net.Proxy; import java.util.HashMap; import java.util.Map; @@ -34,6 +38,8 @@ public class HttpOption extends BaseOption { private RequestEnum requestEnum = RequestEnum.GET; private Map formFields; private Proxy proxy; + private boolean useServerFileName = false; + private IHttpFileLenAdapter fileLenAdapter; public HttpOption() { super(); @@ -131,4 +137,27 @@ public class HttpOption extends BaseOption { this.proxy = proxy; return this; } + + /** + * 是否使用服务器通过content-disposition传递的文件名,内容格式{@code attachment;filename=***} + * 如果获取不到服务器文件名,则使用用户设置的文件名 + * + * @param use {@code true} 使用 + */ + public HttpOption useServerFileName(boolean use) { + this.useServerFileName = use; + return this; + } + + /** + * 如果你需要使用header中特定的key来设置文件长度,或有定制文件长度的需要,那么你可以通过该方法自行处理文件长度 + */ + public HttpOption setFileLenAdapter(IHttpFileLenAdapter fileLenAdapter) { + if (fileLenAdapter == null) { + throw new IllegalArgumentException("adapter为空"); + } + CheckUtil.checkMemberClass(fileLenAdapter.getClass()); + this.fileLenAdapter = fileLenAdapter; + return this; + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index fd7ab2ae..0ebcb131 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -22,6 +22,7 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.CancelAllCmd; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.common.AbsBuilderTarget; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.download.target.DTargetFactory; import com.arialyy.aria.core.download.target.FtpBuilderTarget; @@ -193,7 +194,7 @@ public class DownloadReceiver extends AbsReceiver { } } } else { - ALog.w(TAG, "没有Aria的注解方法"); + ALog.e(TAG, "没有Aria的注解方法"); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index b622dd56..6bb56047 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -44,7 +44,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(mConfigHandler.getUrl())); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(mConfigHandler.getUrl())); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index 95ec9264..3b6cad55 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -78,7 +78,7 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(url)); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(url)); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index e657475c..a6fae105 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -74,7 +74,7 @@ public class FtpDirNormalTarget extends AbsNormalTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index aec82b9e..1513e408 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -44,7 +44,7 @@ public class FtpNormalTarget extends AbsNormalTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index c0e3e100..2a93fd04 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -19,13 +19,10 @@ import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CheckUtil; import java.util.List; /** @@ -146,18 +143,4 @@ public class GroupBuilderTarget extends AbsBuilderTarget { public GroupBuilderTarget setSubFileName(List subTaskFileName) { return mConfigHandler.setSubFileName(subTaskFileName); } - - /** - * 如果你需要使用header中特定的key来设置文件长度,或有定制文件长度的需要,那么你可以通过该方法自行处理文件长度 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public GroupBuilderTarget setFileLenAdapter(IHttpFileLenAdapter adapter) { - - if (adapter == null) { - throw new IllegalArgumentException("adapter为空"); - } - CheckUtil.checkMemberClass(adapter.getClass()); - getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); - return this; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index 213a8886..eed6b2b3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -72,18 +72,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { return this; } - /** - * 是否使用服务器通过content-disposition传递的文件名,内容格式{@code attachment;filename=***} - * 如果获取不到服务器文件名,则使用用户设置的文件名 - * - * @param use {@code true} 使用 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpBuilderTarget useServerFileName(boolean use) { - getTaskWrapper().getOptionParams().setParams(IOptionConstant.useServerFileName, use); - return this; - } - /** * 设置文件存储路径,如果需要修改新的文件名,修改路径便可。 * 如:原文件路径 /mnt/sdcard/test.zip @@ -111,17 +99,4 @@ public class HttpBuilderTarget extends AbsBuilderTarget { mConfigHandler.setForceDownload(forceDownload); return this; } - - /** - * 如果你需要使用header中特定的key来设置文件长度,或有定制文件长度的需要,那么你可以通过该方法自行处理文件长度 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpBuilderTarget setFileLenAdapter(IHttpFileLenAdapter adapter) { - if (adapter == null) { - throw new IllegalArgumentException("adapter为空"); - } - CheckUtil.checkMemberClass(adapter.getClass()); - getTaskWrapper().getOptionParams().setObjs(IOptionConstant.fileLenAdapter, adapter); - return this; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 50995ee0..f0cc0ff9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -270,7 +270,7 @@ public class UploadReceiver extends AbsReceiver { } } } else { - ALog.i(TAG, "没有Aria的注解方法"); + ALog.e(TAG, "没有Aria的注解方法"); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index 1f7d3f91..38749062 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -16,13 +16,10 @@ package com.arialyy.aria.core.upload.target; import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; /** @@ -51,15 +48,6 @@ public class FtpBuilderTarget extends AbsBuilderTarget { return this; } - /** - * FTP文件上传拦截器,如果远端已有同名文件,可使用该拦截器控制覆盖文件或修改该文件上传到服务器端端的文件名 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpBuilderTarget setUploadInterceptor(@NonNull IFtpUploadInterceptor uploadInterceptor) { - CheckUtil.checkMemberClass(uploadInterceptor.getClass()); - return mConfigHandler.setUploadInterceptor(uploadInterceptor); - } - /** * 设置登陆、字符串编码、ftps等参数 */ @@ -68,7 +56,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(url)); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(url)); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index 15a5e88e..8b9f946a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -43,7 +43,7 @@ public class FtpNormalTarget extends AbsNormalTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setFtpUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); + option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getUrl())); getTaskWrapper().getOptionParams().setParams(option); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java index b24c172d..e06e6b05 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java @@ -62,15 +62,6 @@ class UNormalConfigHandler implements IConfigHandler { mEntity.setFileSize(file.length()); } - TARGET setUploadInterceptor(IFtpUploadInterceptor uploadInterceptor) { - if (uploadInterceptor == null) { - throw new NullPointerException("ftp拦截器为空"); - } - getTaskWrapper().getOptionParams() - .setObjs(IOptionConstant.uploadInterceptor, uploadInterceptor); - return mTarget; - } - @Override public AbsEntity getEntity() { return mEntity; } diff --git a/AriaAnnotations/bintray-release.gradle b/AriaAnnotations/bintray-release.gradle index 0e8fef08..91049599 100644 --- a/AriaAnnotations/bintray-release.gradle +++ b/AriaAnnotations/bintray-release.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.novoda.bintray-release' publish { - artifactId = 'aria-annotations' +// artifactId = 'aria-annotations' +// uploadName = 'AriaAnnotations' + artifactId = 'annotations' + uploadName = 'Annotations' userOrg = rootProject.ext.userOrg groupId = rootProject.ext.groupId - // uploadName = rootProject.uploadName - uploadName = 'AriaAnnotations' publishVersion = rootProject.ext.publishVersion desc = rootProject.ext.desc website = rootProject.ext.website diff --git a/AriaCompiler/bintray-release.gradle b/AriaCompiler/bintray-release.gradle index b833f212..839a216f 100644 --- a/AriaCompiler/bintray-release.gradle +++ b/AriaCompiler/bintray-release.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.novoda.bintray-release' publish { - artifactId = 'aria-compiler' +// artifactId = 'aria-compiler' +// uploadName = 'AriaCompiler' + artifactId = 'compiler' + uploadName = 'Compiler' userOrg = rootProject.ext.userOrg groupId = rootProject.ext.groupId - // uploadName = rootProject.uploadName - uploadName = 'AriaCompiler' publishVersion = rootProject.ext.publishVersion desc = rootProject.ext.desc website = rootProject.ext.website diff --git a/AriaCompiler/build.gradle b/AriaCompiler/build.gradle index 167d083a..62aad962 100644 --- a/AriaCompiler/build.gradle +++ b/AriaCompiler/build.gradle @@ -8,8 +8,9 @@ targetCompatibility = JavaVersion.VERSION_1_7 dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.google.auto:auto-common:0.6' - implementation 'com.google.auto.service:auto-service:1.0-rc2' + implementation 'com.google.auto:auto-common:0.10' + implementation 'com.google.auto.service:auto-service:1.0-rc6' + annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' implementation 'com.squareup:javapoet:1.9.0' implementation project(':AriaAnnotations') } diff --git a/FtpComponent/bintray-release.gradle b/FtpComponent/bintray-release.gradle new file mode 100644 index 00000000..8f9782b7 --- /dev/null +++ b/FtpComponent/bintray-release.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.novoda.bintray-release' +publish { +// artifactId = 'aria-compiler' +// uploadName = 'AriaCompiler' + artifactId = 'ftpComponent' + uploadName = 'FtpComponent' + userOrg = rootProject.ext.userOrg + groupId = rootProject.ext.groupId + publishVersion = rootProject.ext.publishVersion + desc = rootProject.ext.desc + website = rootProject.ext.website + licences = rootProject.ext.licences +} \ No newline at end of file diff --git a/FtpComponent/build.gradle b/FtpComponent/build.gradle index 9417b467..7974c103 100644 --- a/FtpComponent/build.gradle +++ b/FtpComponent/build.gradle @@ -27,7 +27,8 @@ dependencies { implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" - testImplementation 'junit:junit:4.12' - implementation project(path: ':AriaFtpPlug') +// implementation project(path: ':AriaFtpPlug') implementation project(path: ':PublicComponent') } + +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketClient.java new file mode 100644 index 00000000..2ddf9be4 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketClient.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.nio.charset.Charset; + +/*** + * The DatagramSocketClient provides the basic operations that are required + * of client objects accessing datagram sockets. It is meant to be + * subclassed to avoid having to rewrite the same code over and over again + * to open a socket, close a socket, set timeouts, etc. Of special note + * is the {@link #setDatagramSocketFactory setDatagramSocketFactory } + * method, which allows you to control the type of DatagramSocket the + * DatagramSocketClient creates for network communications. This is + * especially useful for adding things like proxy support as well as better + * support for applets. For + * example, you could create a + * {@link DatagramSocketFactory} + * that + * requests browser security capabilities before creating a socket. + * All classes derived from DatagramSocketClient should use the + * {@link #_socketFactory_ _socketFactory_ } member variable to + * create DatagramSocket instances rather than instantiating + * them by directly invoking a constructor. By honoring this contract + * you guarantee that a user will always be able to provide his own + * Socket implementations by substituting his own SocketFactory. + * + * + * @see DatagramSocketFactory + ***/ + +public abstract class DatagramSocketClient { + /*** + * The default DatagramSocketFactory shared by all DatagramSocketClient + * instances. + ***/ + private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY = + new DefaultDatagramSocketFactory(); + + /** + * Charset to use for byte IO. + */ + private Charset charset = Charset.defaultCharset(); + + /*** The timeout to use after opening a socket. ***/ + protected int _timeout_; + + /*** The datagram socket used for the connection. ***/ + protected DatagramSocket _socket_; + + /*** + * A status variable indicating if the client's socket is currently open. + ***/ + protected boolean _isOpen_; + + /*** The datagram socket's DatagramSocketFactory. ***/ + protected DatagramSocketFactory _socketFactory_; + + /*** + * Default constructor for DatagramSocketClient. Initializes + * _socket_ to null, _timeout_ to 0, and _isOpen_ to false. + ***/ + public DatagramSocketClient() { + _socket_ = null; + _timeout_ = 0; + _isOpen_ = false; + _socketFactory_ = __DEFAULT_SOCKET_FACTORY; + } + + /*** + * Opens a DatagramSocket on the local host at the first available port. + * Also sets the timeout on the socket to the default timeout set + * by {@link #setDefaultTimeout setDefaultTimeout() }. + *

+ * _isOpen_ is set to true after calling this method and _socket_ + * is set to the newly opened socket. + * + * @throws SocketException If the socket could not be opened or the + * timeout could not be set. + ***/ + public void open() throws SocketException { + _socket_ = _socketFactory_.createDatagramSocket(); + _socket_.setSoTimeout(_timeout_); + _isOpen_ = true; + } + + /*** + * Opens a DatagramSocket on the local host at a specified port. + * Also sets the timeout on the socket to the default timeout set + * by {@link #setDefaultTimeout setDefaultTimeout() }. + *

+ * _isOpen_ is set to true after calling this method and _socket_ + * is set to the newly opened socket. + * + * @param port The port to use for the socket. + * @throws SocketException If the socket could not be opened or the + * timeout could not be set. + ***/ + public void open(int port) throws SocketException { + _socket_ = _socketFactory_.createDatagramSocket(port); + _socket_.setSoTimeout(_timeout_); + _isOpen_ = true; + } + + /*** + * Opens a DatagramSocket at the specified address on the local host + * at a specified port. + * Also sets the timeout on the socket to the default timeout set + * by {@link #setDefaultTimeout setDefaultTimeout() }. + *

+ * _isOpen_ is set to true after calling this method and _socket_ + * is set to the newly opened socket. + * + * @param port The port to use for the socket. + * @param laddr The local address to use. + * @throws SocketException If the socket could not be opened or the + * timeout could not be set. + ***/ + public void open(int port, InetAddress laddr) throws SocketException { + _socket_ = _socketFactory_.createDatagramSocket(port, laddr); + _socket_.setSoTimeout(_timeout_); + _isOpen_ = true; + } + + /*** + * Closes the DatagramSocket used for the connection. + * You should call this method after you've finished using the class + * instance and also before you call {@link #open open() } + * again. _isOpen_ is set to false and _socket_ is set to null. + ***/ + public void close() { + if (_socket_ != null) { + _socket_.close(); + } + _socket_ = null; + _isOpen_ = false; + } + + /*** + * Returns true if the client has a currently open socket. + * + * @return True if the client has a currently open socket, false otherwise. + ***/ + public boolean isOpen() { + return _isOpen_; + } + + /*** + * Set the default timeout in milliseconds to use when opening a socket. + * After a call to open, the timeout for the socket is set using this value. + * This method should be used prior to a call to {@link #open open()} + * and should not be confused with {@link #setSoTimeout setSoTimeout()} + * which operates on the currently open socket. _timeout_ contains + * the new timeout value. + * + * @param timeout The timeout in milliseconds to use for the datagram socket + * connection. + ***/ + public void setDefaultTimeout(int timeout) { + _timeout_ = timeout; + } + + /*** + * Returns the default timeout in milliseconds that is used when + * opening a socket. + * + * @return The default timeout in milliseconds that is used when + * opening a socket. + ***/ + public int getDefaultTimeout() { + return _timeout_; + } + + /*** + * Set the timeout in milliseconds of a currently open connection. + * Only call this method after a connection has been opened + * by {@link #open open()}. + * + * @param timeout The timeout in milliseconds to use for the currently + * open datagram socket connection. + * @throws SocketException if an error setting the timeout + ***/ + public void setSoTimeout(int timeout) throws SocketException { + _socket_.setSoTimeout(timeout); + } + + /*** + * Returns the timeout in milliseconds of the currently opened socket. + * If you call this method when the client socket is not open, + * a NullPointerException is thrown. + * + * @return The timeout in milliseconds of the currently opened socket. + * @throws SocketException if an error getting the timeout + ***/ + public int getSoTimeout() throws SocketException { + return _socket_.getSoTimeout(); + } + + /*** + * Returns the port number of the open socket on the local host used + * for the connection. If you call this method when the client socket + * is not open, a NullPointerException is thrown. + * + * @return The port number of the open socket on the local host used + * for the connection. + ***/ + public int getLocalPort() { + return _socket_.getLocalPort(); + } + + /*** + * Returns the local address to which the client's socket is bound. + * If you call this method when the client socket is not open, a + * NullPointerException is thrown. + * + * @return The local address to which the client's socket is bound. + ***/ + public InetAddress getLocalAddress() { + return _socket_.getLocalAddress(); + } + + /*** + * Sets the DatagramSocketFactory used by the DatagramSocketClient + * to open DatagramSockets. If the factory value is null, then a default + * factory is used (only do this to reset the factory after having + * previously altered it). + * + * @param factory The new DatagramSocketFactory the DatagramSocketClient + * should use. + ***/ + public void setDatagramSocketFactory(DatagramSocketFactory factory) { + if (factory == null) { + _socketFactory_ = __DEFAULT_SOCKET_FACTORY; + } else { + _socketFactory_ = factory; + } + } + + /** + * Gets the charset name. + * + * @return the charset name. + * @since 3.3 + * TODO Will be deprecated once the code requires Java 1.6 as a mininmum + */ + public String getCharsetName() { + return charset.name(); + } + + /** + * Gets the charset. + * + * @return the charset. + * @since 3.3 + */ + public Charset getCharset() { + return charset; + } + + /** + * Sets the charset. + * + * @param charset the charset. + * @since 3.3 + */ + public void setCharset(Charset charset) { + this.charset = charset; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java new file mode 100644 index 00000000..385cceb4 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +/*** + * The DatagramSocketFactory interface provides a means for the + * programmer to control the creation of datagram sockets and + * provide his own DatagramSocket implementations for use by all + * classes derived from + * {@link DatagramSocketClient} + * . + * This allows you to provide your own DatagramSocket implementations and + * to perform security checks or browser capability requests before + * creating a DatagramSocket. + * + * + ***/ + +public interface DatagramSocketFactory { + + /*** + * Creates a DatagramSocket on the local host at the first available port. + * @return the socket + * + * @throws SocketException If the socket could not be created. + ***/ + public DatagramSocket createDatagramSocket() throws SocketException; + + /*** + * Creates a DatagramSocket on the local host at a specified port. + * + * @param port The port to use for the socket. + * @return the socket + * @throws SocketException If the socket could not be created. + ***/ + public DatagramSocket createDatagramSocket(int port) throws SocketException; + + /*** + * Creates a DatagramSocket at the specified address on the local host + * at a specified port. + * + * @param port The port to use for the socket. + * @param laddr The local address to use. + * @return the socket + * @throws SocketException If the socket could not be created. + ***/ + public DatagramSocket createDatagramSocket(int port, InetAddress laddr) throws SocketException; +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java new file mode 100644 index 00000000..e269124d --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +/*** + * DefaultDatagramSocketFactory implements the DatagramSocketFactory + * interface by simply wrapping the java.net.DatagramSocket + * constructors. It is the default DatagramSocketFactory used by + * {@link DatagramSocketClient} + * implementations. + * + * + * @see DatagramSocketFactory + * @see DatagramSocketClient + * @see DatagramSocketClient#setDatagramSocketFactory + ***/ + +public class DefaultDatagramSocketFactory implements DatagramSocketFactory { + + /*** + * Creates a DatagramSocket on the local host at the first available port. + * @return a new DatagramSocket + * @throws SocketException If the socket could not be created. + ***/ + @Override public DatagramSocket createDatagramSocket() throws SocketException { + return new DatagramSocket(); + } + + /*** + * Creates a DatagramSocket on the local host at a specified port. + * + * @param port The port to use for the socket. + * @return a new DatagramSocket + * @throws SocketException If the socket could not be created. + ***/ + @Override public DatagramSocket createDatagramSocket(int port) throws SocketException { + return new DatagramSocket(port); + } + + /*** + * Creates a DatagramSocket at the specified address on the local host + * at a specified port. + * + * @param port The port to use for the socket. + * @param laddr The local address to use. + * @return a new DatagramSocket + * @throws SocketException If the socket could not be created. + ***/ + @Override public DatagramSocket createDatagramSocket(int port, InetAddress laddr) + throws SocketException { + return new DatagramSocket(port, laddr); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java new file mode 100644 index 00000000..1fec9085 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.SocketFactory; + +/*** + * DefaultSocketFactory implements the SocketFactory interface by + * simply wrapping the java.net.Socket and java.net.ServerSocket + * constructors. It is the default SocketFactory used by + * {@link SocketClient} + * implementations. + * + * + * @see SocketFactory + * @see SocketClient + * @see SocketClient#setSocketFactory + ***/ + +public class DefaultSocketFactory extends SocketFactory { + /** The proxy to use when creating new sockets. */ + private final Proxy connProxy; + + /** + * The default constructor. + */ + public DefaultSocketFactory() { + this(null); + } + + /** + * A constructor for sockets with proxy support. + * + * @param proxy The Proxy to use when creating new Sockets. + * @since 3.2 + */ + public DefaultSocketFactory(Proxy proxy) { + connProxy = proxy; + } + + /** + * Creates an unconnected Socket. + * + * @return A new unconnected Socket. + * @throws IOException If an I/O error occurs while creating the Socket. + * @since 3.2 + */ + @Override public Socket createSocket() throws IOException { + if (connProxy != null) { + return new Socket(connProxy); + } + return new Socket(); + } + + /*** + * Creates a Socket connected to the given host and port. + * + * @param host The hostname to connect to. + * @param port The port to connect to. + * @return A Socket connected to the given host and port. + * @throws UnknownHostException If the hostname cannot be resolved. + * @throws IOException If an I/O error occurs while creating the Socket. + ***/ + @Override public Socket createSocket(String host, int port) + throws UnknownHostException, IOException { + if (connProxy != null) { + Socket s = new Socket(connProxy); + s.connect(new InetSocketAddress(host, port)); + return s; + } + return new Socket(host, port); + } + + /*** + * Creates a Socket connected to the given host and port. + * + * @param address The address of the host to connect to. + * @param port The port to connect to. + * @return A Socket connected to the given host and port. + * @throws IOException If an I/O error occurs while creating the Socket. + ***/ + @Override public Socket createSocket(InetAddress address, int port) throws IOException { + if (connProxy != null) { + Socket s = new Socket(connProxy); + s.connect(new InetSocketAddress(address, port)); + return s; + } + return new Socket(address, port); + } + + /*** + * Creates a Socket connected to the given host and port and + * originating from the specified local address and port. + * + * @param host The hostname to connect to. + * @param port The port to connect to. + * @param localAddr The local address to use. + * @param localPort The local port to use. + * @return A Socket connected to the given host and port. + * @throws UnknownHostException If the hostname cannot be resolved. + * @throws IOException If an I/O error occurs while creating the Socket. + ***/ + @Override public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) + throws UnknownHostException, IOException { + if (connProxy != null) { + Socket s = new Socket(connProxy); + s.bind(new InetSocketAddress(localAddr, localPort)); + s.connect(new InetSocketAddress(host, port)); + return s; + } + return new Socket(host, port, localAddr, localPort); + } + + /*** + * Creates a Socket connected to the given host and port and + * originating from the specified local address and port. + * + * @param address The address of the host to connect to. + * @param port The port to connect to. + * @param localAddr The local address to use. + * @param localPort The local port to use. + * @return A Socket connected to the given host and port. + * @throws IOException If an I/O error occurs while creating the Socket. + ***/ + @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddr, + int localPort) throws IOException { + if (connProxy != null) { + Socket s = new Socket(connProxy); + s.bind(new InetSocketAddress(localAddr, localPort)); + s.connect(new InetSocketAddress(address, port)); + return s; + } + return new Socket(address, port, localAddr, localPort); + } + + /*** + * Creates a ServerSocket bound to a specified port. A port + * of 0 will create the ServerSocket on a system-determined free port. + * + * @param port The port on which to listen, or 0 to use any free port. + * @return A ServerSocket that will listen on a specified port. + * @throws IOException If an I/O error occurs while creating + * the ServerSocket. + ***/ + public ServerSocket createServerSocket(int port) throws IOException { + return new ServerSocket(port); + } + + /*** + * Creates a ServerSocket bound to a specified port with a given + * maximum queue length for incoming connections. A port of 0 will + * create the ServerSocket on a system-determined free port. + * + * @param port The port on which to listen, or 0 to use any free port. + * @param backlog The maximum length of the queue for incoming connections. + * @return A ServerSocket that will listen on a specified port. + * @throws IOException If an I/O error occurs while creating + * the ServerSocket. + ***/ + public ServerSocket createServerSocket(int port, int backlog) throws IOException { + return new ServerSocket(port, backlog); + } + + /*** + * Creates a ServerSocket bound to a specified port on a given local + * address with a given maximum queue length for incoming connections. + * A port of 0 will + * create the ServerSocket on a system-determined free port. + * + * @param port The port on which to listen, or 0 to use any free port. + * @param backlog The maximum length of the queue for incoming connections. + * @param bindAddr The local address to which the ServerSocket should bind. + * @return A ServerSocket that will listen on a specified port. + * @throws IOException If an I/O error occurs while creating + * the ServerSocket. + ***/ + public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException { + return new ServerSocket(port, backlog, bindAddr); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java b/FtpComponent/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java new file mode 100644 index 00000000..c63818fc --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.io.IOException; + +/*** + * This exception is used to indicate that the reply from a server + * could not be interpreted. Most of the NetComponents classes attempt + * to be as lenient as possible when receiving server replies. Many + * server implementations deviate from IETF protocol specifications, making + * it necessary to be as flexible as possible. However, there will be + * certain situations where it is not possible to continue an operation + * because the server reply could not be interpreted in a meaningful manner. + * In these cases, a MalformedServerReplyException should be thrown. + * + * + ***/ + +public class MalformedServerReplyException extends IOException { + + private static final long serialVersionUID = 6006765264250543945L; + + /*** Constructs a MalformedServerReplyException with no message ***/ + public MalformedServerReplyException() { + super(); + } + + /*** + * Constructs a MalformedServerReplyException with a specified message. + * + * @param message The message explaining the reason for the exception. + ***/ + public MalformedServerReplyException(String message) { + super(message); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/PrintCommandListener.java b/FtpComponent/src/main/java/aria/apache/commons/net/PrintCommandListener.java new file mode 100644 index 00000000..14f92c78 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/PrintCommandListener.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/*** + * This is a support class for some of the example programs. It is + * a sample implementation of the ProtocolCommandListener interface + * which just prints out to a specified stream all command/reply traffic. + * + * @since 2.0 + ***/ + +public class PrintCommandListener implements ProtocolCommandListener { + private final PrintWriter __writer; + private final boolean __nologin; + private final char __eolMarker; + private final boolean __directionMarker; + + /** + * Create the default instance which prints everything. + * + * @param stream where to write the commands and responses + * e.g. System.out + * @since 3.0 + */ + public PrintCommandListener(PrintStream stream) { + this(new PrintWriter(stream)); + } + + /** + * Create an instance which optionally suppresses login command text + * and indicates where the EOL starts with the specified character. + * + * @param stream where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @since 3.0 + */ + public PrintCommandListener(PrintStream stream, boolean suppressLogin) { + this(new PrintWriter(stream), suppressLogin); + } + + /** + * Create an instance which optionally suppresses login command text + * and indicates where the EOL starts with the specified character. + * + * @param stream where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @param eolMarker if non-zero, add a marker just before the EOL. + * @since 3.0 + */ + public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker) { + this(new PrintWriter(stream), suppressLogin, eolMarker); + } + + /** + * Create an instance which optionally suppresses login command text + * and indicates where the EOL starts with the specified character. + * + * @param stream where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @param eolMarker if non-zero, add a marker just before the EOL. + * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the + * output + * @since 3.0 + */ + public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker, + boolean showDirection) { + this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection); + } + + /** + * Create the default instance which prints everything. + * + * @param writer where to write the commands and responses + */ + public PrintCommandListener(PrintWriter writer) { + this(writer, false); + } + + /** + * Create an instance which optionally suppresses login command text. + * + * @param writer where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @since 3.0 + */ + public PrintCommandListener(PrintWriter writer, boolean suppressLogin) { + this(writer, suppressLogin, (char) 0); + } + + /** + * Create an instance which optionally suppresses login command text + * and indicates where the EOL starts with the specified character. + * + * @param writer where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @param eolMarker if non-zero, add a marker just before the EOL. + * @since 3.0 + */ + public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker) { + this(writer, suppressLogin, eolMarker, false); + } + + /** + * Create an instance which optionally suppresses login command text + * and indicates where the EOL starts with the specified character. + * + * @param writer where to write the commands and responses + * @param suppressLogin if {@code true}, only print command name for login + * @param eolMarker if non-zero, add a marker just before the EOL. + * @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the + * output + * @since 3.0 + */ + public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker, + boolean showDirection) { + __writer = writer; + __nologin = suppressLogin; + __eolMarker = eolMarker; + __directionMarker = showDirection; + } + + @Override public void protocolCommandSent(ProtocolCommandEvent event) { + if (__directionMarker) { + __writer.print("> "); + } + if (__nologin) { + String cmd = event.getCommand(); + if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) { + __writer.print(cmd); + __writer.println(" *******"); // Don't bother with EOL marker for this! + } else { + final String IMAP_LOGIN = "LOGIN"; + if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP + String msg = event.getMessage(); + msg = msg.substring(0, msg.indexOf(IMAP_LOGIN) + IMAP_LOGIN.length()); + __writer.print(msg); + __writer.println(" *******"); // Don't bother with EOL marker for this! + } else { + __writer.print(getPrintableString(event.getMessage())); + } + } + } else { + __writer.print(getPrintableString(event.getMessage())); + } + __writer.flush(); + } + + private String getPrintableString(String msg) { + if (__eolMarker == 0) { + return msg; + } + int pos = msg.indexOf(SocketClient.NETASCII_EOL); + if (pos > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(msg.substring(0, pos)); + sb.append(__eolMarker); + sb.append(msg.substring(pos)); + return sb.toString(); + } + return msg; + } + + @Override public void protocolReplyReceived(ProtocolCommandEvent event) { + if (__directionMarker) { + __writer.print("< "); + } + __writer.print(event.getMessage()); + __writer.flush(); + } +} + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java new file mode 100644 index 00000000..9f0154e7 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.util.EventObject; + +/*** + * There exists a large class of IETF protocols that work by sending an + * ASCII text command and arguments to a server, and then receiving an + * ASCII text reply. For debugging and other purposes, it is extremely + * useful to log or keep track of the contents of the protocol messages. + * The ProtocolCommandEvent class coupled with the + * {@link ProtocolCommandListener} + * interface facilitate this process. + * + * + * @see ProtocolCommandListener + * @see ProtocolCommandSupport + ***/ + +public class ProtocolCommandEvent extends EventObject { + private static final long serialVersionUID = 403743538418947240L; + + private final int __replyCode; + private final boolean __isCommand; + private final String __message, __command; + + /*** + * Creates a ProtocolCommandEvent signalling a command was sent to + * the server. ProtocolCommandEvents created with this constructor + * should only be sent after a command has been sent, but before the + * reply has been received. + * + * @param source The source of the event. + * @param command The string representation of the command type sent, not + * including the arguments (e.g., "STAT" or "GET"). + * @param message The entire command string verbatim as sent to the server, + * including all arguments. + ***/ + public ProtocolCommandEvent(Object source, String command, String message) { + super(source); + __replyCode = 0; + __message = message; + __isCommand = true; + __command = command; + } + + /*** + * Creates a ProtocolCommandEvent signalling a reply to a command was + * received. ProtocolCommandEvents created with this constructor + * should only be sent after a complete command reply has been received + * fromt a server. + * + * @param source The source of the event. + * @param replyCode The integer code indicating the natureof the reply. + * This will be the protocol integer value for protocols + * that use integer reply codes, or the reply class constant + * corresponding to the reply for protocols like POP3 that use + * strings like OK rather than integer codes (i.e., POP3Repy.OK). + * @param message The entire reply as received from the server. + ***/ + public ProtocolCommandEvent(Object source, int replyCode, String message) { + super(source); + __replyCode = replyCode; + __message = message; + __isCommand = false; + __command = null; + } + + /*** + * Returns the string representation of the command type sent (e.g., "STAT" + * or "GET"). If the ProtocolCommandEvent is a reply event, then null + * is returned. + * + * @return The string representation of the command type sent, or null + * if this is a reply event. + ***/ + public String getCommand() { + return __command; + } + + /*** + * Returns the reply code of the received server reply. Undefined if + * this is not a reply event. + * + * @return The reply code of the received server reply. Undefined if + * not a reply event. + ***/ + public int getReplyCode() { + return __replyCode; + } + + /*** + * Returns true if the ProtocolCommandEvent was generated as a result + * of sending a command. + * + * @return true If the ProtocolCommandEvent was generated as a result + * of sending a command. False otherwise. + ***/ + public boolean isCommand() { + return __isCommand; + } + + /*** + * Returns true if the ProtocolCommandEvent was generated as a result + * of receiving a reply. + * + * @return true If the ProtocolCommandEvent was generated as a result + * of receiving a reply. False otherwise. + ***/ + public boolean isReply() { + return !isCommand(); + } + + /*** + * Returns the entire message sent to or received from the server. + * Includes the line terminator. + * + * @return The entire message sent to or received from the server. + ***/ + public String getMessage() { + return __message; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java new file mode 100644 index 00000000..12f8e00e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import aria.apache.commons.net.ftp.FTPClient; +import java.util.EventListener; + +/*** + * There exists a large class of IETF protocols that work by sending an + * ASCII text command and arguments to a server, and then receiving an + * ASCII text reply. For debugging and other purposes, it is extremely + * useful to log or keep track of the contents of the protocol messages. + * The ProtocolCommandListener interface coupled with the + * {@link ProtocolCommandEvent} class facilitate this process. + *

+ * To receive ProtocolCommandEvents, you merely implement the + * ProtocolCommandListener interface and register the class as a listener + * with a ProtocolCommandEvent source such as + * {@link FTPClient}. + * + * + * @see ProtocolCommandEvent + * @see ProtocolCommandSupport + ***/ + +public interface ProtocolCommandListener extends EventListener { + + /*** + * This method is invoked by a ProtocolCommandEvent source after + * sending a protocol command to a server. + * + * @param event The ProtocolCommandEvent fired. + ***/ + public void protocolCommandSent(ProtocolCommandEvent event); + + /*** + * This method is invoked by a ProtocolCommandEvent source after + * receiving a reply from a server. + * + * @param event The ProtocolCommandEvent fired. + ***/ + public void protocolReplyReceived(ProtocolCommandEvent event); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java new file mode 100644 index 00000000..01b1888e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.io.Serializable; +import java.util.EventListener; + +import aria.apache.commons.net.util.ListenerList; + +/*** + * ProtocolCommandSupport is a convenience class for managing a list of + * ProtocolCommandListeners and firing ProtocolCommandEvents. You can + * simply delegate ProtocolCommandEvent firing and listener + * registering/unregistering tasks to this class. + * + * + * @see ProtocolCommandEvent + * @see ProtocolCommandListener + ***/ + +public class ProtocolCommandSupport implements Serializable { + private static final long serialVersionUID = -8017692739988399978L; + + private final Object __source; + private final ListenerList __listeners; + + /*** + * Creates a ProtocolCommandSupport instance using the indicated source + * as the source of ProtocolCommandEvents. + * + * @param source The source to use for all generated ProtocolCommandEvents. + ***/ + public ProtocolCommandSupport(Object source) { + __listeners = new ListenerList(); + __source = source; + } + + /*** + * Fires a ProtocolCommandEvent signalling the sending of a command to all + * registered listeners, invoking their + * {@link ProtocolCommandListener#protocolCommandSent protocolCommandSent() } + * methods. + * + * @param command The string representation of the command type sent, not + * including the arguments (e.g., "STAT" or "GET"). + * @param message The entire command string verbatim as sent to the server, + * including all arguments. + ***/ + public void fireCommandSent(String command, String message) { + ProtocolCommandEvent event; + + event = new ProtocolCommandEvent(__source, command, message); + + for (EventListener listener : __listeners) { + ((ProtocolCommandListener) listener).protocolCommandSent(event); + } + } + + /*** + * Fires a ProtocolCommandEvent signalling the reception of a command reply + * to all registered listeners, invoking their + * {@link ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() } + * methods. + * + * @param replyCode The integer code indicating the natureof the reply. + * This will be the protocol integer value for protocols + * that use integer reply codes, or the reply class constant + * corresponding to the reply for protocols like POP3 that use + * strings like OK rather than integer codes (i.e., POP3Repy.OK). + * @param message The entire reply as received from the server. + ***/ + public void fireReplyReceived(int replyCode, String message) { + ProtocolCommandEvent event; + event = new ProtocolCommandEvent(__source, replyCode, message); + + for (EventListener listener : __listeners) { + ((ProtocolCommandListener) listener).protocolReplyReceived(event); + } + } + + /*** + * Adds a ProtocolCommandListener. + * + * @param listener The ProtocolCommandListener to add. + ***/ + public void addProtocolCommandListener(ProtocolCommandListener listener) { + __listeners.addListener(listener); + } + + /*** + * Removes a ProtocolCommandListener. + * + * @param listener The ProtocolCommandListener to remove. + ***/ + public void removeProtocolCommandListener(ProtocolCommandListener listener) { + __listeners.removeListener(listener); + } + + /*** + * Returns the number of ProtocolCommandListeners currently registered. + * + * @return The number of ProtocolCommandListeners currently registered. + ***/ + public int getListenerCount() { + return __listeners.getListenerCount(); + } +} + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/SocketClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/SocketClient.java new file mode 100644 index 00000000..0ac183a7 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/SocketClient.java @@ -0,0 +1,869 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.net.SocketException; +import java.nio.charset.Charset; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + +/** + * The SocketClient provides the basic operations that are required of + * client objects accessing sockets. It is meant to be + * subclassed to avoid having to rewrite the same code over and over again + * to open a socket, close a socket, set timeouts, etc. Of special note + * is the {@link #setSocketFactory setSocketFactory } + * method, which allows you to control the type of Socket the SocketClient + * creates for initiating network connections. This is especially useful + * for adding SSL or proxy support as well as better support for applets. For + * example, you could create a + * {@link SocketFactory} that + * requests browser security capabilities before creating a socket. + * All classes derived from SocketClient should use the + * {@link #_socketFactory_ _socketFactory_ } member variable to + * create Socket and ServerSocket instances rather than instantiating + * them by directly invoking a constructor. By honoring this contract + * you guarantee that a user will always be able to provide his own + * Socket implementations by substituting his own SocketFactory. + * + * @see SocketFactory + */ +public abstract class SocketClient { + /** + * The end of line character sequence used by most IETF protocols. That + * is a carriage return followed by a newline: "\r\n" + */ + public static final String NETASCII_EOL = "\r\n"; + + /** The default SocketFactory shared by all SocketClient instances. */ + private static final SocketFactory __DEFAULT_SOCKET_FACTORY = SocketFactory.getDefault(); + + /** The default {@link ServerSocketFactory} */ + private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = + ServerSocketFactory.getDefault(); + + /** + * A ProtocolCommandSupport object used to manage the registering of + * ProtocolCommandListeners and the firing of ProtocolCommandEvents. + */ + private ProtocolCommandSupport __commandSupport; + + /** The timeout to use after opening a socket. */ + protected int _timeout_; + + /** The socket used for the connection. */ + protected Socket _socket_; + + /** The hostname used for the connection (null = no hostname supplied). */ + protected String _hostname_; + + /** The default port the client should connect to. */ + protected int _defaultPort_; + + /** The socket's InputStream. */ + protected InputStream _input_; + + /** The socket's OutputStream. */ + protected OutputStream _output_; + + /** The socket's SocketFactory. */ + protected SocketFactory _socketFactory_; + + /** The socket's ServerSocket Factory. */ + protected ServerSocketFactory _serverSocketFactory_; + + /** The socket's connect timeout (0 = infinite timeout) */ + private static final int DEFAULT_CONNECT_TIMEOUT = 0; + protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; + + /** Hint for SO_RCVBUF size */ + private int receiveBufferSize = -1; + + /** Hint for SO_SNDBUF size */ + private int sendBufferSize = -1; + + /** The proxy to use when connecting. */ + private Proxy connProxy; + + /** + * Charset to use for byte IO. + */ + private Charset charset = Charset.defaultCharset(); + + /** + * Default constructor for SocketClient. Initializes + * _socket_ to null, _timeout_ to 0, _defaultPort to 0, + * _isConnected_ to false, charset to {@code Charset.defaultCharset()} + * and _socketFactory_ to a shared instance of + * {@link DefaultSocketFactory}. + */ + public SocketClient() { + _socket_ = null; + _hostname_ = null; + _input_ = null; + _output_ = null; + _timeout_ = 0; + _defaultPort_ = 0; + _socketFactory_ = __DEFAULT_SOCKET_FACTORY; + _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; + } + + /** + * Because there are so many connect() methods, the _connectAction_() + * method is provided as a means of performing some action immediately + * after establishing a connection, rather than reimplementing all + * of the connect() methods. The last action performed by every + * connect() method after opening a socket is to call this method. + *

+ * This method sets the timeout on the just opened socket to the default + * timeout set by {@link #setDefaultTimeout setDefaultTimeout() }, + * sets _input_ and _output_ to the socket's InputStream and OutputStream + * respectively, and sets _isConnected_ to true. + *

+ * Subclasses overriding this method should start by calling + * super._connectAction_() first to ensure the + * initialization of the aforementioned protected variables. + * + * @throws IOException (SocketException) if a problem occurs with the socket + */ + protected void _connectAction_() throws IOException { + _socket_.setSoTimeout(_timeout_); + _input_ = _socket_.getInputStream(); + _output_ = _socket_.getOutputStream(); + } + + /** + * Opens a Socket connected to a remote host at the specified port and + * originating from the current host at a system assigned port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param host The remote host. + * @param port The port to connect to on the remote host. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + */ + public void connect(InetAddress host, int port) throws SocketException, IOException { + _hostname_ = null; + _connect(host, port, null, -1); + } + + /** + * Opens a Socket connected to a remote host at the specified port and + * originating from the current host at a system assigned port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param hostname The name of the remote host. + * @param port The port to connect to on the remote host. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + * @throws java.net.UnknownHostException If the hostname cannot be resolved. + */ + public void connect(String hostname, int port) throws SocketException, IOException { + _hostname_ = hostname; + _connect(InetAddress.getByName(hostname), port, null, -1); + } + + /** + * Opens a Socket connected to a remote host at the specified port and + * originating from the specified local address and port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param host The remote host. + * @param port The port to connect to on the remote host. + * @param localAddr The local address to use. + * @param localPort The local port to use. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + */ + public void connect(InetAddress host, int port, InetAddress localAddr, int localPort) + throws SocketException, IOException { + _hostname_ = null; + _connect(host, port, localAddr, localPort); + } + + // helper method to allow code to be shared with connect(String,...) methods + private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort) + throws SocketException, IOException { + _socket_ = _socketFactory_.createSocket(); + if (receiveBufferSize != -1) { + _socket_.setReceiveBufferSize(receiveBufferSize); + } + if (sendBufferSize != -1) { + _socket_.setSendBufferSize(sendBufferSize); + } + if (localAddr != null) { + _socket_.bind(new InetSocketAddress(localAddr, localPort)); + } + _socket_.connect(new InetSocketAddress(host, port), connectTimeout); + _connectAction_(); + } + + /** + * Opens a Socket connected to a remote host at the specified port and + * originating from the specified local address and port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param hostname The name of the remote host. + * @param port The port to connect to on the remote host. + * @param localAddr The local address to use. + * @param localPort The local port to use. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + * @throws java.net.UnknownHostException If the hostname cannot be resolved. + */ + public void connect(String hostname, int port, InetAddress localAddr, int localPort) + throws SocketException, IOException { + _hostname_ = hostname; + _connect(InetAddress.getByName(hostname), port, localAddr, localPort); + } + + /** + * Opens a Socket connected to a remote host at the current default port + * and originating from the current host at a system assigned port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param host The remote host. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + */ + public void connect(InetAddress host) throws SocketException, IOException { + _hostname_ = null; + connect(host, _defaultPort_); + } + + /** + * Opens a Socket connected to a remote host at the current default + * port and originating from the current host at a system assigned port. + * Before returning, {@link #_connectAction_ _connectAction_() } + * is called to perform connection initialization actions. + *

+ * + * @param hostname The name of the remote host. + * @throws SocketException If the socket timeout could not be set. + * @throws IOException If the socket could not be opened. In most + * cases you will only want to catch IOException since SocketException is + * derived from it. + * @throws java.net.UnknownHostException If the hostname cannot be resolved. + */ + public void connect(String hostname) throws SocketException, IOException { + connect(hostname, _defaultPort_); + } + + /** + * Disconnects the socket connection. + * You should call this method after you've finished using the class + * instance and also before you call + * {@link #connect connect() } + * again. _isConnected_ is set to false, _socket_ is set to null, + * _input_ is set to null, and _output_ is set to null. + *

+ * + * @throws IOException If there is an error closing the socket. + */ + public void disconnect() throws IOException { + closeQuietly(_socket_); + closeQuietly(_input_); + closeQuietly(_output_); + _socket_ = null; + _hostname_ = null; + _input_ = null; + _output_ = null; + } + + private void closeQuietly(Socket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // Ignored + } + } + } + + private void closeQuietly(Closeable close) { + if (close != null) { + try { + close.close(); + } catch (IOException e) { + // Ignored + } + } + } + + /** + * Returns true if the client is currently connected to a server. + *

+ * Delegates to {@link Socket#isConnected()} + * + * @return True if the client is currently connected to a server, + * false otherwise. + */ + public boolean isConnected() { + if (_socket_ == null) { + return false; + } + + return _socket_.isConnected(); + } + + /** + * Make various checks on the socket to test if it is available for use. + * Note that the only sure test is to use it, but these checks may help + * in some cases. + * + * @return {@code true} if the socket appears to be available for use + * @see NET-350 + * @since 3.0 + */ + public boolean isAvailable() { + if (isConnected()) { + try { + if (_socket_.getInetAddress() == null) { + return false; + } + if (_socket_.getPort() == 0) { + return false; + } + if (_socket_.getRemoteSocketAddress() == null) { + return false; + } + if (_socket_.isClosed()) { + return false; + } + /* these aren't exact checks (a Socket can be half-open), + but since we usually require two-way data transfer, + we check these here too: */ + if (_socket_.isInputShutdown()) { + return false; + } + if (_socket_.isOutputShutdown()) { + return false; + } + /* ignore the result, catch exceptions: */ + _socket_.getInputStream(); + _socket_.getOutputStream(); + } catch (IOException ioex) { + return false; + } + return true; + } else { + return false; + } + } + + /** + * Sets the default port the SocketClient should connect to when a port + * is not specified. The {@link #_defaultPort_ _defaultPort_ } + * variable stores this value. If never set, the default port is equal + * to zero. + *

+ * + * @param port The default port to set. + */ + public void setDefaultPort(int port) { + _defaultPort_ = port; + } + + /** + * Returns the current value of the default port (stored in + * {@link #_defaultPort_ _defaultPort_ }). + *

+ * + * @return The current value of the default port. + */ + public int getDefaultPort() { + return _defaultPort_; + } + + /** + * Set the default timeout in milliseconds to use when opening a socket. + * This value is only used previous to a call to + * {@link #connect connect()} + * and should not be confused with {@link #setSoTimeout setSoTimeout()} + * which operates on an the currently opened socket. _timeout_ contains + * the new timeout value. + *

+ * + * @param timeout The timeout in milliseconds to use for the socket + * connection. + */ + public void setDefaultTimeout(int timeout) { + _timeout_ = timeout; + } + + /** + * Returns the default timeout in milliseconds that is used when + * opening a socket. + *

+ * + * @return The default timeout in milliseconds that is used when + * opening a socket. + */ + public int getDefaultTimeout() { + return _timeout_; + } + + /** + * Set the timeout in milliseconds of a currently open connection. + * Only call this method after a connection has been opened + * by {@link #connect connect()}. + *

+ * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. + * + * @param timeout The timeout in milliseconds to use for the currently + * open socket connection. + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public void setSoTimeout(int timeout) throws SocketException { + _socket_.setSoTimeout(timeout); + } + + /** + * Set the underlying socket send buffer size. + *

+ * + * @param size The size of the buffer in bytes. + * @throws SocketException never thrown, but subclasses might want to do so + * @since 2.0 + */ + public void setSendBufferSize(int size) throws SocketException { + sendBufferSize = size; + } + + /** + * Get the current sendBuffer size + * + * @return the size, or -1 if not initialised + * @since 3.0 + */ + protected int getSendBufferSize() { + return sendBufferSize; + } + + /** + * Sets the underlying socket receive buffer size. + *

+ * + * @param size The size of the buffer in bytes. + * @throws SocketException never (but subclasses may wish to do so) + * @since 2.0 + */ + public void setReceiveBufferSize(int size) throws SocketException { + receiveBufferSize = size; + } + + /** + * Get the current receivedBuffer size + * + * @return the size, or -1 if not initialised + * @since 3.0 + */ + protected int getReceiveBufferSize() { + return receiveBufferSize; + } + + /** + * Returns the timeout in milliseconds of the currently opened socket. + *

+ * + * @return The timeout in milliseconds of the currently opened socket. + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public int getSoTimeout() throws SocketException { + return _socket_.getSoTimeout(); + } + + /** + * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the + * currently opened socket. + *

+ * + * @param on True if Nagle's algorithm is to be enabled, false if not. + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public void setTcpNoDelay(boolean on) throws SocketException { + _socket_.setTcpNoDelay(on); + } + + /** + * Returns true if Nagle's algorithm is enabled on the currently opened + * socket. + *

+ * + * @return True if Nagle's algorithm is enabled on the currently opened + * socket, false otherwise. + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public boolean getTcpNoDelay() throws SocketException { + return _socket_.getTcpNoDelay(); + } + + /** + * Sets the SO_KEEPALIVE flag on the currently opened socket. + * + * From the Javadocs, the default keepalive time is 2 hours (although this is + * implementation dependent). It looks as though the Windows WSA sockets implementation + * allows a specific keepalive value to be set, although this seems not to be the case on + * other systems. + * + * @param keepAlive If true, keepAlive is turned on + * @throws SocketException if there is a problem with the socket + * @throws NullPointerException if the socket is not currently open + * @since 2.2 + */ + public void setKeepAlive(boolean keepAlive) throws SocketException { + _socket_.setKeepAlive(keepAlive); + } + + /** + * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. + * Delegates to {@link Socket#getKeepAlive()} + * + * @return True if SO_KEEPALIVE is enabled. + * @throws SocketException if there is a problem with the socket + * @throws NullPointerException if the socket is not currently open + * @since 2.2 + */ + public boolean getKeepAlive() throws SocketException { + return _socket_.getKeepAlive(); + } + + /** + * Sets the SO_LINGER timeout on the currently opened socket. + *

+ * + * @param on True if linger is to be enabled, false if not. + * @param val The linger timeout (in hundredths of a second?) + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public void setSoLinger(boolean on, int val) throws SocketException { + _socket_.setSoLinger(on, val); + } + + /** + * Returns the current SO_LINGER timeout of the currently opened socket. + *

+ * + * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns + * -1. + * @throws SocketException If the operation fails. + * @throws NullPointerException if the socket is not currently open + */ + public int getSoLinger() throws SocketException { + return _socket_.getSoLinger(); + } + + /** + * Returns the port number of the open socket on the local host used + * for the connection. + * Delegates to {@link Socket#getLocalPort()} + *

+ * + * @return The port number of the open socket on the local host used + * for the connection. + * @throws NullPointerException if the socket is not currently open + */ + public int getLocalPort() { + return _socket_.getLocalPort(); + } + + /** + * Returns the local address to which the client's socket is bound. + * Delegates to {@link Socket#getLocalAddress()} + *

+ * + * @return The local address to which the client's socket is bound. + * @throws NullPointerException if the socket is not currently open + */ + public InetAddress getLocalAddress() { + return _socket_.getLocalAddress(); + } + + /** + * Returns the port number of the remote host to which the client is + * connected. + * Delegates to {@link Socket#getPort()} + *

+ * + * @return The port number of the remote host to which the client is + * connected. + * @throws NullPointerException if the socket is not currently open + */ + public int getRemotePort() { + return _socket_.getPort(); + } + + /** + * @return The remote address to which the client is connected. + * Delegates to {@link Socket#getInetAddress()} + * @throws NullPointerException if the socket is not currently open + */ + public InetAddress getRemoteAddress() { + return _socket_.getInetAddress(); + } + + /** + * Verifies that the remote end of the given socket is connected to the + * the same host that the SocketClient is currently connected to. This + * is useful for doing a quick security check when a client needs to + * accept a connection from a server, such as an FTP data connection or + * a BSD R command standard error stream. + *

+ * + * @param socket the item to check against + * @return True if the remote hosts are the same, false if not. + */ + public boolean verifyRemote(Socket socket) { + InetAddress host1, host2; + + host1 = socket.getInetAddress(); + host2 = getRemoteAddress(); + + return host1.equals(host2); + } + + /** + * Sets the SocketFactory used by the SocketClient to open socket + * connections. If the factory value is null, then a default + * factory is used (only do this to reset the factory after having + * previously altered it). + * Any proxy setting is discarded. + *

+ * + * @param factory The new SocketFactory the SocketClient should use. + */ + public void setSocketFactory(SocketFactory factory) { + if (factory == null) { + _socketFactory_ = __DEFAULT_SOCKET_FACTORY; + } else { + _socketFactory_ = factory; + } + // re-setting the socket factory makes the proxy setting useless, + // so set the field to null so that getProxy() doesn't return a + // Proxy that we're actually not using. + connProxy = null; + } + + /** + * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket + * connections. If the factory value is null, then a default + * factory is used (only do this to reset the factory after having + * previously altered it). + *

+ * + * @param factory The new ServerSocketFactory the SocketClient should use. + * @since 2.0 + */ + public void setServerSocketFactory(ServerSocketFactory factory) { + if (factory == null) { + _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; + } else { + _serverSocketFactory_ = factory; + } + } + + /** + * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} + * object's + * connect() method. + * + * @param connectTimeout The connection timeout to use (in ms) + * @since 2.0 + */ + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + /** + * Get the underlying socket connection timeout. + * + * @return timeout (in ms) + * @since 2.0 + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Get the underlying {@link ServerSocketFactory} + * + * @return The server socket factory + * @since 2.2 + */ + public ServerSocketFactory getServerSocketFactory() { + return _serverSocketFactory_; + } + + /** + * Adds a ProtocolCommandListener. + * + * @param listener The ProtocolCommandListener to add. + * @since 3.0 + */ + public void addProtocolCommandListener(ProtocolCommandListener listener) { + getCommandSupport().addProtocolCommandListener(listener); + } + + /** + * Removes a ProtocolCommandListener. + * + * @param listener The ProtocolCommandListener to remove. + * @since 3.0 + */ + public void removeProtocolCommandListener(ProtocolCommandListener listener) { + getCommandSupport().removeProtocolCommandListener(listener); + } + + /** + * If there are any listeners, send them the reply details. + * + * @param replyCode the code extracted from the reply + * @param reply the full reply text + * @since 3.0 + */ + protected void fireReplyReceived(int replyCode, String reply) { + if (getCommandSupport().getListenerCount() > 0) { + getCommandSupport().fireReplyReceived(replyCode, reply); + } + } + + /** + * If there are any listeners, send them the command details. + * + * @param command the command name + * @param message the complete message, including command name + * @since 3.0 + */ + protected void fireCommandSent(String command, String message) { + if (getCommandSupport().getListenerCount() > 0) { + getCommandSupport().fireCommandSent(command, message); + } + } + + /** + * Create the CommandSupport instance if required + */ + protected void createCommandSupport() { + __commandSupport = new ProtocolCommandSupport(this); + } + + /** + * Subclasses can override this if they need to provide their own + * instance field for backwards compatibilty. + * + * @return the CommandSupport instance, may be {@code null} + * @since 3.0 + */ + protected ProtocolCommandSupport getCommandSupport() { + return __commandSupport; + } + + /** + * Sets the proxy for use with all the connections. + * The proxy is used for connections established after the + * call to this method. + * + * @param proxy the new proxy for connections. + * @since 3.2 + */ + public void setProxy(Proxy proxy) { + setSocketFactory(new DefaultSocketFactory(proxy)); + connProxy = proxy; + } + + /** + * Gets the proxy for use with all the connections. + * + * @return the current proxy for connections. + */ + public Proxy getProxy() { + return connProxy; + } + + /** + * Gets the charset name. + * + * @return the charset. + * @since 3.3 + * @deprecated Since the code now requires Java 1.6 as a mininmum + */ + @Deprecated public String getCharsetName() { + return charset.name(); + } + + /** + * Gets the charset. + * + * @return the charset. + * @since 3.3 + */ + public Charset getCharset() { + return charset; + } + + /** + * Sets the charset. + * + * @param charset the charset. + * @since 3.3 + */ + public void setCharset(Charset charset) { + this.charset = charset; + } + + /* + * N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, + * so the abstract method is needed to pass the instance to the methods which were moved here. + */ +} + + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/Configurable.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/Configurable.java new file mode 100644 index 00000000..b0361f80 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/Configurable.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +/** + * This interface adds the aspect of configurability by means of + * a supplied FTPClientConfig object to other classes in the + * system, especially listing parsers. + */ +public interface Configurable { + + /** + * @param config the object containing the configuration data + * @throws IllegalArgumentException if the elements of the + * config are somehow inadequate to configure the + * Configurable object. + */ + public void configure(FTPClientConfig config); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTP.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTP.java new file mode 100644 index 00000000..12a99b15 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTP.java @@ -0,0 +1,1777 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.ArrayList; + +import aria.apache.commons.net.MalformedServerReplyException; +import aria.apache.commons.net.ProtocolCommandSupport; +import aria.apache.commons.net.SocketClient; +import aria.apache.commons.net.io.CRLFLineReader; + +/*** + * FTP provides the basic the functionality necessary to implement your + * own FTP client. It extends SocketClient since + * extending TelnetClient was causing unwanted behavior (like connections + * that did not time out properly). + *

+ * To derive the full benefits of the FTP class requires some knowledge + * of the FTP protocol defined in RFC 959. However, there is no reason + * why you should have to use the FTP class. The + * {@link FTPClient} class, + * derived from FTP, + * implements all the functionality required of an FTP client. The + * FTP class is made public to provide access to various FTP constants + * and to make it easier for adventurous programmers (or those with + * special needs) to interact with the FTP protocol and implement their + * own clients. A set of methods with names corresponding to the FTP + * command names are provided to facilitate this interaction. + *

+ * You should keep in mind that the FTP server may choose to prematurely + * close a connection if the client has been idle for longer than a + * given time period (usually 900 seconds). The FTP class will detect a + * premature FTP server connection closing when it receives a + * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } + * response to a command. + * When that occurs, the FTP class method encountering that reply will throw + * an {@link FTPConnectionClosedException} + * . FTPConectionClosedException + * is a subclass of IOException and therefore need not be + * caught separately, but if you are going to catch it separately, its + * catch block must appear before the more general IOException + * catch block. When you encounter an + * {@link FTPConnectionClosedException} + * , you must disconnect the connection with + * {@link #disconnect disconnect() } to properly clean up the + * system resources used by FTP. Before disconnecting, you may check the + * last reply code and text with + * {@link #getReplyCode getReplyCode }, + * {@link #getReplyString getReplyString }, + * and {@link #getReplyStrings getReplyStrings}. + * You may avoid server disconnections while the client is idle by + * periodicaly sending NOOP commands to the server. + *

+ * Rather than list it separately for each method, we mention here that + * every method communicating with the server and throwing an IOException + * can also throw a + * {@link MalformedServerReplyException} + * , which is a subclass + * of IOException. A MalformedServerReplyException will be thrown when + * the reply received from the server deviates enough from the protocol + * specification that it cannot be interpreted in a useful manner despite + * attempts to be as lenient as possible. + * + * @see FTPClient + * @see FTPConnectionClosedException + * @see MalformedServerReplyException + * @version $Id: FTP.java 1782546 2017-02-11 00:05:41Z sebb $ + ***/ + +public class FTP extends SocketClient { + /*** The default FTP data port (20). ***/ + public static final int DEFAULT_DATA_PORT = 20; + /*** The default FTP control port (21). ***/ + public static final int DEFAULT_PORT = 21; + + /*** + * A constant used to indicate the file(s) being transferred should + * be treated as ASCII. This is the default file type. All constants + * ending in FILE_TYPE are used to indicate file types. + ***/ + public static final int ASCII_FILE_TYPE = 0; + + /*** + * A constant used to indicate the file(s) being transferred should + * be treated as EBCDIC. Note however that there are several different + * EBCDIC formats. All constants ending in FILE_TYPE + * are used to indicate file types. + ***/ + public static final int EBCDIC_FILE_TYPE = 1; + + /*** + * A constant used to indicate the file(s) being transferred should + * be treated as a binary image, i.e., no translations should be + * performed. All constants ending in FILE_TYPE are used to + * indicate file types. + ***/ + public static final int BINARY_FILE_TYPE = 2; + + /*** + * A constant used to indicate the file(s) being transferred should + * be treated as a local type. All constants ending in + * FILE_TYPE are used to indicate file types. + ***/ + public static final int LOCAL_FILE_TYPE = 3; + + /*** + * A constant used for text files to indicate a non-print text format. + * This is the default format. + * All constants ending in TEXT_FORMAT are used to indicate + * text formatting for text transfers (both ASCII and EBCDIC). + ***/ + public static final int NON_PRINT_TEXT_FORMAT = 4; + + /*** + * A constant used to indicate a text file contains format vertical format + * control characters. + * All constants ending in TEXT_FORMAT are used to indicate + * text formatting for text transfers (both ASCII and EBCDIC). + ***/ + public static final int TELNET_TEXT_FORMAT = 5; + + /*** + * A constant used to indicate a text file contains ASA vertical format + * control characters. + * All constants ending in TEXT_FORMAT are used to indicate + * text formatting for text transfers (both ASCII and EBCDIC). + ***/ + public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6; + + /*** + * A constant used to indicate a file is to be treated as a continuous + * sequence of bytes. This is the default structure. All constants ending + * in _STRUCTURE are used to indicate file structure for + * file transfers. + ***/ + public static final int FILE_STRUCTURE = 7; + + /*** + * A constant used to indicate a file is to be treated as a sequence + * of records. All constants ending in _STRUCTURE + * are used to indicate file structure for file transfers. + ***/ + public static final int RECORD_STRUCTURE = 8; + + /*** + * A constant used to indicate a file is to be treated as a set of + * independent indexed pages. All constants ending in + * _STRUCTURE are used to indicate file structure for file + * transfers. + ***/ + public static final int PAGE_STRUCTURE = 9; + + /*** + * A constant used to indicate a file is to be transferred as a stream + * of bytes. This is the default transfer mode. All constants ending + * in TRANSFER_MODE are used to indicate file transfer + * modes. + ***/ + public static final int STREAM_TRANSFER_MODE = 10; + + /*** + * A constant used to indicate a file is to be transferred as a series + * of blocks. All constants ending in TRANSFER_MODE are used + * to indicate file transfer modes. + ***/ + public static final int BLOCK_TRANSFER_MODE = 11; + + /*** + * A constant used to indicate a file is to be transferred as FTP + * compressed data. All constants ending in TRANSFER_MODE + * are used to indicate file transfer modes. + ***/ + public static final int COMPRESSED_TRANSFER_MODE = 12; + + // We have to ensure that the protocol communication is in ASCII + // but we use ISO-8859-1 just in case 8-bit characters cross + // the wire. + /** + * The default character encoding used for communicating over an + * FTP control connection. The default encoding is an + * ASCII-compatible encoding. Some FTP servers expect other + * encodings. You can change the encoding used by an FTP instance + * with {@link #setControlEncoding setControlEncoding}. + */ + public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1"; + + /** Length of the FTP reply code (3 alphanumerics) */ + public static final int REPLY_CODE_LEN = 3; + + private static final String __modes = "AEILNTCFRPSBC"; + + protected int _replyCode; + protected ArrayList _replyLines; + protected boolean _newReplyString; + protected String _replyString; + protected String _controlEncoding; + + /** + * A ProtocolCommandSupport object used to manage the registering of + * ProtocolCommandListeners and te firing of ProtocolCommandEvents. + */ + protected ProtocolCommandSupport _commandSupport_; + + /** + * This is used to signal whether a block of multiline responses beginning + * with xxx must be terminated by the same numeric code xxx + * See section 4.2 of RFC 959 for details. + */ + protected boolean strictMultilineParsing = false; + + /** + * If this is true, then non-multiline replies must have the format: + * 3 digit code + * If false, then the 3 digit code does not have to be followed by space + * See section 4.2 of RFC 959 for details. + */ + private boolean strictReplyParsing = true; + + /** + * Wraps SocketClient._input_ to facilitate the reading of text + * from the FTP control connection. Do not access the control + * connection via SocketClient._input_. This member starts + * with a null value, is initialized in {@link #_connectAction_}, + * and set to null in {@link #disconnect}. + */ + protected BufferedReader _controlInput_; + + /** + * Wraps SocketClient._output_ to facilitate the writing of text + * to the FTP control connection. Do not access the control + * connection via SocketClient._output_. This member starts + * with a null value, is initialized in {@link #_connectAction_}, + * and set to null in {@link #disconnect}. + */ + protected BufferedWriter _controlOutput_; + + /*** + * The default FTP constructor. Sets the default port to + * DEFAULT_PORT and initializes internal data structures + * for saving FTP reply information. + ***/ + public FTP() { + super(); + setDefaultPort(DEFAULT_PORT); + _replyLines = new ArrayList(); + _newReplyString = false; + _replyString = null; + _controlEncoding = DEFAULT_CONTROL_ENCODING; + _commandSupport_ = new ProtocolCommandSupport(this); + } + + // The RFC-compliant multiline termination check + private boolean __strictCheck(String line, String code) { + return (!(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' ')); + } + + // The strict check is too strong a condition because of non-conforming ftp + // servers like ftp.funet.fi which sent 226 as the last line of a + // 426 multi-line reply in response to ls /. We relax the condition to + // test that the line starts with a digit rather than starting with + // the code. + private boolean __lenientCheck(String line) { + return (!(line.length() > REPLY_CODE_LEN + && line.charAt(REPLY_CODE_LEN) != '-' + && Character.isDigit(line.charAt(0)))); + } + + /** + * Get the reply, and pass it to command listeners + */ + private void __getReply() throws IOException { + __getReply(true); + } + + /** + * Get the reply, but don't pass it to command listeners. + * Used for keep-alive processing only. + * + * @throws IOException on error + * @since 3.0 + */ + protected void __getReplyNoReport() throws IOException { + __getReply(false); + } + + private void __getReply(boolean reportReply) throws IOException { + int length; + + _newReplyString = true; + _replyLines.clear(); + + String line = _controlInput_.readLine(); + + if (line == null) { + throw new FTPConnectionClosedException("Connection closed without indication."); + } + + // In case we run into an anomaly we don't want fatal index exceptions + // to be thrown. + length = line.length(); + if (length < REPLY_CODE_LEN) { + throw new MalformedServerReplyException("Truncated server reply: " + line); + } + + String code = null; + try { + code = line.substring(0, REPLY_CODE_LEN); + _replyCode = Integer.parseInt(code); + } catch (NumberFormatException e) { + throw new MalformedServerReplyException( + "Could not parse response code.\nServer Reply: " + line); + } + + _replyLines.add(line); + + // Check the server reply type + if (length > REPLY_CODE_LEN) { + char sep = line.charAt(REPLY_CODE_LEN); + // Get extra lines if message continues. + if (sep == '-') { + do { + line = _controlInput_.readLine(); + + if (line == null) { + throw new FTPConnectionClosedException("Connection closed without indication."); + } + + _replyLines.add(line); + + // The length() check handles problems that could arise from readLine() + // returning too soon after encountering a naked CR or some other + // anomaly. + } while (isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line)); + } else if (isStrictReplyParsing()) { + if (length == REPLY_CODE_LEN + 1) { // expecting some text + throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); + } else if (sep != ' ') { + throw new MalformedServerReplyException("Invalid server reply: '" + line + "'"); + } + } + } else if (isStrictReplyParsing()) { + throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); + } + + if (reportReply) { + fireReplyReceived(_replyCode, getReplyString()); + } + + if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) { + throw new FTPConnectionClosedException( + "FTP response 421 received. Server closed connection."); + } + } + + /** + * Initiates control connections and gets initial reply. + * Initializes {@link #_controlInput_} and {@link #_controlOutput_}. + */ + @Override protected void _connectAction_() throws IOException { + _connectAction_(null); + } + + /** + * Initiates control connections and gets initial reply. + * Initializes {@link #_controlInput_} and {@link #_controlOutput_}. + * + * @param socketIsReader the reader to reuse (if non-null) + * @throws IOException on error + * @since 3.4 + */ + protected void _connectAction_(Reader socketIsReader) throws IOException { + super._connectAction_(); // sets up _input_ and _output_ + if (socketIsReader == null) { + _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); + } else { + _controlInput_ = new CRLFLineReader(socketIsReader); + } + _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); + if (connectTimeout > 0) { // NET-385 + int original = _socket_.getSoTimeout(); + _socket_.setSoTimeout(connectTimeout); + try { + __getReply(); + // If we received code 120, we have to fetch completion reply. + if (FTPReply.isPositivePreliminary(_replyCode)) { + __getReply(); + } + } catch (SocketTimeoutException e) { + IOException ioe = new IOException("Timed out waiting for initial connect reply"); + ioe.initCause(e); + throw ioe; + } finally { + _socket_.setSoTimeout(original); + } + } else { + __getReply(); + // If we received code 120, we have to fetch completion reply. + if (FTPReply.isPositivePreliminary(_replyCode)) { + __getReply(); + } + } + } + + /** + * Saves the character encoding to be used by the FTP control connection. + * Some FTP servers require that commands be issued in a non-ASCII + * encoding like UTF-8 so that filenames with multi-byte character + * representations (e.g, Big 8) can be specified. + *

+ * Please note that this has to be set before the connection is established. + * + * @param encoding The new character encoding for the control connection. + */ + public void setControlEncoding(String encoding) { + _controlEncoding = encoding; + } + + /** + * @return The character encoding used to communicate over the + * control connection. + */ + public String getControlEncoding() { + return _controlEncoding; + } + + /*** + * Closes the control connection to the FTP server and sets to null + * some internal data so that the memory may be reclaimed by the + * garbage collector. The reply text and code information from the + * last command is voided so that the memory it used may be reclaimed. + * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null. + * + * @throws IOException If an error occurs while disconnecting. + ***/ + @Override public void disconnect() throws IOException { + super.disconnect(); + _controlInput_ = null; + _controlOutput_ = null; + _newReplyString = false; + _replyString = null; + } + + /*** + * Sends an FTP command to the server, waits for a reply and returns the + * numerical response code. After invocation, for more detailed + * information, the actual reply text can be accessed by calling + * {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The text representation of the FTP command to send. + * @param args The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int sendCommand(String command, String args) throws IOException { + if (_controlOutput_ == null) { + throw new IOException("Connection is not open"); + } + + final String message = __buildMessage(command, args); + + __send(message); + + fireCommandSent(command, message); + + __getReply(); + return _replyCode; + } + + private String __buildMessage(String command, String args) { + final StringBuilder __commandBuffer = new StringBuilder(); + + __commandBuffer.append(command); + + if (args != null) { + __commandBuffer.append(' '); + __commandBuffer.append(args); + } + __commandBuffer.append(SocketClient.NETASCII_EOL); + return __commandBuffer.toString(); + } + + private void __send(String message) + throws IOException, FTPConnectionClosedException, SocketException { + try { + _controlOutput_.write(message); + _controlOutput_.flush(); + } catch (SocketException e) { + if (!isConnected()) { + throw new FTPConnectionClosedException("Connection unexpectedly closed."); + } else { + throw e; + } + } + } + + /** + * Send a noop and get the reply without reporting to the command listener. + * Intended for use with keep-alive. + * + * @throws IOException on error + * @since 3.0 + */ + protected void __noop() throws IOException { + String msg = __buildMessage(FTPCmd.NOOP.getCommand(), null); + __send(msg); + __getReplyNoReport(); // This may timeout + } + + /*** + * Sends an FTP command to the server, waits for a reply and returns the + * numerical response code. After invocation, for more detailed + * information, the actual reply text can be accessed by calling + * {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The FTPCommand constant corresponding to the FTP command + * to send. + * @param args The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead + ***/ + @Deprecated public int sendCommand(int command, String args) throws IOException { + return sendCommand(FTPCommand.getCommand(command), args); + } + + /** + * Sends an FTP command to the server, waits for a reply and returns the + * numerical response code. After invocation, for more detailed + * information, the actual reply text can be accessed by calling + * {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The FTPCmd enum corresponding to the FTP command + * to send. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.3 + */ + public int sendCommand(FTPCmd command) throws IOException { + return sendCommand(command, null); + } + + /** + * Sends an FTP command to the server, waits for a reply and returns the + * numerical response code. After invocation, for more detailed + * information, the actual reply text can be accessed by calling + * {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The FTPCmd enum corresponding to the FTP command + * to send. + * @param args The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.3 + */ + public int sendCommand(FTPCmd command, String args) throws IOException { + return sendCommand(command.getCommand(), args); + } + + /*** + * Sends an FTP command with no arguments to the server, waits for a + * reply and returns the numerical response code. After invocation, for + * more detailed information, the actual reply text can be accessed by + * calling {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The text representation of the FTP command to send. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int sendCommand(String command) throws IOException { + return sendCommand(command, null); + } + + /*** + * Sends an FTP command with no arguments to the server, waits for a + * reply and returns the numerical response code. After invocation, for + * more detailed information, the actual reply text can be accessed by + * calling {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. + * + * @param command The FTPCommand constant corresponding to the FTP command + * to send. + * @return The integer value of the FTP reply code returned by the server + * in response to the command. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int sendCommand(int command) throws IOException { + return sendCommand(command, null); + } + + /*** + * Returns the integer value of the reply code of the last FTP reply. + * You will usually only use this method after you connect to the + * FTP server to check that the connection was successful since + * connect is of type void. + * + * @return The integer value of the reply code of the last FTP reply. + ***/ + public int getReplyCode() { + return _replyCode; + } + + /*** + * Fetches a reply from the FTP server and returns the integer reply + * code. After calling this method, the actual reply text can be accessed + * from either calling {@link #getReplyString getReplyString } or + * {@link #getReplyStrings getReplyStrings }. Only use this + * method if you are implementing your own FTP client or if you need to + * fetch a secondary response from the FTP server. + * + * @return The integer value of the reply code of the fetched FTP reply. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while receiving the + * server reply. + ***/ + public int getReply() throws IOException { + __getReply(); + return _replyCode; + } + + /*** + * Returns the lines of text from the last FTP server response as an array + * of strings, one entry per line. The end of line markers of each are + * stripped from each line. + * + * @return The lines of text from the last FTP response as an array. + ***/ + public String[] getReplyStrings() { + return _replyLines.toArray(new String[_replyLines.size()]); + } + + /*** + * Returns the entire text of the last FTP server response exactly + * as it was received, including all end of line markers in NETASCII + * format. + * + * @return The entire text from the last FTP response as a String. + ***/ + public String getReplyString() { + StringBuilder buffer; + + if (!_newReplyString) { + return _replyString; + } + + buffer = new StringBuilder(256); + + for (String line : _replyLines) { + buffer.append(line); + buffer.append(SocketClient.NETASCII_EOL); + } + + _newReplyString = false; + + return (_replyString = buffer.toString()); + } + + /*** + * A convenience method to send the FTP USER command to the server, + * receive the reply, and return the reply code. + * + * @param username The username to login under. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int user(String username) throws IOException { + return sendCommand(FTPCmd.USER, username); + } + + /** + * A convenience method to send the FTP PASS command to the server, + * receive the reply, and return the reply code. + * + * @param password The plain text password of the username being logged into. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + */ + public int pass(String password) throws IOException { + return sendCommand(FTPCmd.PASS, password); + } + + /*** + * A convenience method to send the FTP ACCT command to the server, + * receive the reply, and return the reply code. + * + * @param account The account name to access. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int acct(String account) throws IOException { + return sendCommand(FTPCmd.ACCT, account); + } + + /*** + * A convenience method to send the FTP ABOR command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int abor() throws IOException { + return sendCommand(FTPCmd.ABOR); + } + + /*** + * A convenience method to send the FTP CWD command to the server, + * receive the reply, and return the reply code. + * + * @param directory The new working directory. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int cwd(String directory) throws IOException { + return sendCommand(FTPCmd.CWD, directory); + } + + /*** + * A convenience method to send the FTP CDUP command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int cdup() throws IOException { + return sendCommand(FTPCmd.CDUP); + } + + /*** + * A convenience method to send the FTP QUIT command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int quit() throws IOException { + return sendCommand(FTPCmd.QUIT); + } + + /*** + * A convenience method to send the FTP REIN command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int rein() throws IOException { + return sendCommand(FTPCmd.REIN); + } + + /*** + * A convenience method to send the FTP SMNT command to the server, + * receive the reply, and return the reply code. + * + * @param dir The directory name. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int smnt(String dir) throws IOException { + return sendCommand(FTPCmd.SMNT, dir); + } + + /*** + * A convenience method to send the FTP PORT command to the server, + * receive the reply, and return the reply code. + * + * @param host The host owning the port. + * @param port The new port. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int port(InetAddress host, int port) throws IOException { + int num; + StringBuilder info = new StringBuilder(24); + + info.append(host.getHostAddress().replace('.', ',')); + num = port >>> 8; + info.append(','); + info.append(num); + info.append(','); + num = port & 0xff; + info.append(num); + + return sendCommand(FTPCmd.PORT, info.toString()); + } + + /*** + * A convenience method to send the FTP EPRT command to the server, + * receive the reply, and return the reply code. + * + * Examples: + *

    + *
  • EPRT |1|132.235.1.2|6275|
  • + *
  • EPRT |2|1080::8:800:200C:417A|5282|
  • + *
+ * + * @see "http://www.faqs.org/rfcs/rfc2428.html" + * + * @param host The host owning the port. + * @param port The new port. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 2.2 + ***/ + public int eprt(InetAddress host, int port) throws IOException { + int num; + StringBuilder info = new StringBuilder(); + String h; + + // If IPv6, trim the zone index + h = host.getHostAddress(); + num = h.indexOf("%"); + if (num > 0) { + h = h.substring(0, num); + } + + info.append("|"); + + if (host instanceof Inet4Address) { + info.append("1"); + } else if (host instanceof Inet6Address) { + info.append("2"); + } + info.append("|"); + info.append(h); + info.append("|"); + info.append(port); + info.append("|"); + + return sendCommand(FTPCmd.EPRT, info.toString()); + } + + /*** + * A convenience method to send the FTP PASV command to the server, + * receive the reply, and return the reply code. Remember, it's up + * to you to interpret the reply string containing the host/port + * information. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int pasv() throws IOException { + return sendCommand(FTPCmd.PASV); + } + + /*** + * A convenience method to send the FTP EPSV command to the server, + * receive the reply, and return the reply code. Remember, it's up + * to you to interpret the reply string containing the host/port + * information. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 2.2 + ***/ + public int epsv() throws IOException { + return sendCommand(FTPCmd.EPSV); + } + + /** + * A convenience method to send the FTP TYPE command for text files + * to the server, receive the reply, and return the reply code. + * + * @param fileType The type of the file (one of the FILE_TYPE + * constants). + * @param formatOrByteSize The format of the file (one of the + * _FORMAT constants. In the case of + * LOCAL_FILE_TYPE, the byte size. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + */ + public int type(int fileType, int formatOrByteSize) throws IOException { + StringBuilder arg = new StringBuilder(); + + arg.append(__modes.charAt(fileType)); + arg.append(' '); + if (fileType == LOCAL_FILE_TYPE) { + arg.append(formatOrByteSize); + } else { + arg.append(__modes.charAt(formatOrByteSize)); + } + + return sendCommand(FTPCmd.TYPE, arg.toString()); + } + + /** + * A convenience method to send the FTP TYPE command to the server, + * receive the reply, and return the reply code. + * + * @param fileType The type of the file (one of the FILE_TYPE + * constants). + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + */ + public int type(int fileType) throws IOException { + return sendCommand(FTPCmd.TYPE, __modes.substring(fileType, fileType + 1)); + } + + /*** + * A convenience method to send the FTP STRU command to the server, + * receive the reply, and return the reply code. + * + * @param structure The structure of the file (one of the + * _STRUCTURE constants). + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int stru(int structure) throws IOException { + return sendCommand(FTPCmd.STRU, __modes.substring(structure, structure + 1)); + } + + /*** + * A convenience method to send the FTP MODE command to the server, + * receive the reply, and return the reply code. + * + * @param mode The transfer mode to use (one of the + * TRANSFER_MODE constants). + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int mode(int mode) throws IOException { + return sendCommand(FTPCmd.MODE, __modes.substring(mode, mode + 1)); + } + + /*** + * A convenience method to send the FTP RETR command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param pathname The pathname of the file to retrieve. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int retr(String pathname) throws IOException { + return sendCommand(FTPCmd.RETR, pathname); + } + + /*** + * A convenience method to send the FTP STOR command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param pathname The pathname to use for the file when stored at + * the remote end of the transfer. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int stor(String pathname) throws IOException { + return sendCommand(FTPCmd.STOR, pathname); + } + + /*** + * A convenience method to send the FTP STOU command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int stou() throws IOException { + return sendCommand(FTPCmd.STOU); + } + + /*** + * A convenience method to send the FTP STOU command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * @param pathname The base pathname to use for the file when stored at + * the remote end of the transfer. Some FTP servers + * require this. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + */ + public int stou(String pathname) throws IOException { + return sendCommand(FTPCmd.STOU, pathname); + } + + /*** + * A convenience method to send the FTP APPE command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param pathname The pathname to use for the file when stored at + * the remote end of the transfer. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int appe(String pathname) throws IOException { + return sendCommand(FTPCmd.APPE, pathname); + } + + /*** + * A convenience method to send the FTP ALLO command to the server, + * receive the reply, and return the reply code. + * + * @param bytes The number of bytes to allocate. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int allo(int bytes) throws IOException { + return sendCommand(FTPCmd.ALLO, Integer.toString(bytes)); + } + + /** + * A convenience method to send the FTP FEAT command to the server, receive the reply, + * and return the reply code. + * + * @return The reply code received by the server + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 2.2 + */ + public int feat() throws IOException { + return sendCommand(FTPCmd.FEAT); + } + + /*** + * A convenience method to send the FTP ALLO command to the server, + * receive the reply, and return the reply code. + * + * @param bytes The number of bytes to allocate. + * @param recordSize The size of a record. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int allo(int bytes, int recordSize) throws IOException { + return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " + Integer.toString(recordSize)); + } + + /*** + * A convenience method to send the FTP REST command to the server, + * receive the reply, and return the reply code. + * + * @param marker The marker at which to restart a transfer. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int rest(String marker) throws IOException { + return sendCommand(FTPCmd.REST, marker); + } + + /** + * @param file name of file + * @return the status + * @throws IOException on error + * @since 2.0 + **/ + public int mdtm(String file) throws IOException { + return sendCommand(FTPCmd.MDTM, file); + } + + /** + * A convenience method to send the FTP MFMT command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname for which mtime is to be changed + * @param timeval Timestamp in YYYYMMDDhhmmss format + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 + * @since 2.2 + **/ + public int mfmt(String pathname, String timeval) throws IOException { + return sendCommand(FTPCmd.MFMT, timeval + " " + pathname); + } + + /*** + * A convenience method to send the FTP RNFR command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname to rename from. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int rnfr(String pathname) throws IOException { + return sendCommand(FTPCmd.RNFR, pathname); + } + + /*** + * A convenience method to send the FTP RNTO command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname to rename to + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int rnto(String pathname) throws IOException { + return sendCommand(FTPCmd.RNTO, pathname); + } + + /*** + * A convenience method to send the FTP DELE command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname to delete. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int dele(String pathname) throws IOException { + return sendCommand(FTPCmd.DELE, pathname); + } + + /*** + * A convenience method to send the FTP RMD command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname of the directory to remove. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int rmd(String pathname) throws IOException { + return sendCommand(FTPCmd.RMD, pathname); + } + + /*** + * A convenience method to send the FTP MKD command to the server, + * receive the reply, and return the reply code. + * + * @param pathname The pathname of the new directory to create. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int mkd(String pathname) throws IOException { + return sendCommand(FTPCmd.MKD, pathname); + } + + /*** + * A convenience method to send the FTP PWD command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int pwd() throws IOException { + return sendCommand(FTPCmd.PWD); + } + + /*** + * A convenience method to send the FTP LIST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int list() throws IOException { + return sendCommand(FTPCmd.LIST); + } + + /*** + * A convenience method to send the FTP LIST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param pathname The pathname to list, + * may be {@code null} in which case the command is sent with no parameters + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int list(String pathname) throws IOException { + return sendCommand(FTPCmd.LIST, pathname); + } + + /** + * A convenience method to send the FTP MLSD command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.0 + */ + public int mlsd() throws IOException { + return sendCommand(FTPCmd.MLSD); + } + + /** + * A convenience method to send the FTP MLSD command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param path the path to report on + * @return The reply code received from the server, + * may be {@code null} in which case the command is sent with no parameters + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.0 + */ + public int mlsd(String path) throws IOException { + return sendCommand(FTPCmd.MLSD, path); + } + + /** + * A convenience method to send the FTP MLST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.0 + */ + public int mlst() throws IOException { + return sendCommand(FTPCmd.MLST); + } + + /** + * A convenience method to send the FTP MLST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param path the path to report on + * @return The reply code received from the server, + * may be {@code null} in which case the command is sent with no parameters + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + * @since 3.0 + */ + public int mlst(String path) throws IOException { + return sendCommand(FTPCmd.MLST, path); + } + + /*** + * A convenience method to send the FTP NLST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int nlst() throws IOException { + return sendCommand(FTPCmd.NLST); + } + + /*** + * A convenience method to send the FTP NLST command to the server, + * receive the reply, and return the reply code. Remember, it is up + * to you to manage the data connection. If you don't need this low + * level of access, use {@link FTPClient} + * , which will handle all low level details for you. + * + * @param pathname The pathname to list, + * may be {@code null} in which case the command is sent with no parameters + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int nlst(String pathname) throws IOException { + return sendCommand(FTPCmd.NLST, pathname); + } + + /*** + * A convenience method to send the FTP SITE command to the server, + * receive the reply, and return the reply code. + * + * @param parameters The site parameters to send. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int site(String parameters) throws IOException { + return sendCommand(FTPCmd.SITE, parameters); + } + + /*** + * A convenience method to send the FTP SYST command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int syst() throws IOException { + return sendCommand(FTPCmd.SYST); + } + + /*** + * A convenience method to send the FTP STAT command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int stat() throws IOException { + return sendCommand(FTPCmd.STAT); + } + + /*** + * A convenience method to send the FTP STAT command to the server, + * receive the reply, and return the reply code. + * + * @param pathname A pathname to list. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int stat(String pathname) throws IOException { + return sendCommand(FTPCmd.STAT, pathname); + } + + /*** + * A convenience method to send the FTP HELP command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int help() throws IOException { + return sendCommand(FTPCmd.HELP); + } + + /*** + * A convenience method to send the FTP HELP command to the server, + * receive the reply, and return the reply code. + * + * @param command The command name on which to request help. + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int help(String command) throws IOException { + return sendCommand(FTPCmd.HELP, command); + } + + /*** + * A convenience method to send the FTP NOOP command to the server, + * receive the reply, and return the reply code. + * + * @return The reply code received from the server. + * @throws FTPConnectionClosedException + * If the FTP server prematurely closes the connection as a result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending the + * command or receiving the server reply. + ***/ + public int noop() throws IOException { + return sendCommand(FTPCmd.NOOP); + } + + /** + * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2. + * + * @return True if strict, false if lenient + * @since 2.0 + */ + public boolean isStrictMultilineParsing() { + return strictMultilineParsing; + } + + /** + * Set strict multiline parsing. + * + * @param strictMultilineParsing the setting + * @since 2.0 + */ + public void setStrictMultilineParsing(boolean strictMultilineParsing) { + this.strictMultilineParsing = strictMultilineParsing; + } + + /** + * Return whether strict non-multiline parsing is enabled, as per RFC 959, section 4.2. + *

+ * The default is true, which requires the 3 digit code be followed by space and some text. + *
+ * If false, only the 3 digit code is required (as was the case for versions up to 3.5) + *
+ * + * @return True if strict (default), false if additional checks are not made + * @since 3.6 + */ + public boolean isStrictReplyParsing() { + return strictReplyParsing; + } + + /** + * Set strict non-multiline parsing. + *

+ * If true, it requires the 3 digit code be followed by space and some text. + *
+ * If false, only the 3 digit code is required (as was the case for versions up to 3.5) + *

+ * This should not be required by a well-behaved FTP server + *
+ * + * @param strictReplyParsing the setting + * @since 3.6 + */ + public void setStrictReplyParsing(boolean strictReplyParsing) { + this.strictReplyParsing = strictReplyParsing; + } + + /** + * Provide command support to super-class + */ + @Override protected ProtocolCommandSupport getCommandSupport() { + return _commandSupport_; + } +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java new file mode 100644 index 00000000..4bcf98dc --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java @@ -0,0 +1,3787 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aria.apache.commons.net.ftp; + +import aria.apache.commons.net.SocketClient; +import aria.apache.commons.net.ftp.parser.ParserInitializationException; +import aria.apache.commons.net.io.CopyStreamAdapter; +import aria.apache.commons.net.io.CopyStreamEvent; +import aria.apache.commons.net.io.CopyStreamException; +import aria.apache.commons.net.io.CopyStreamListener; +import aria.apache.commons.net.io.FromNetASCIIInputStream; +import aria.apache.commons.net.io.SocketInputStream; +import aria.apache.commons.net.io.SocketOutputStream; +import aria.apache.commons.net.io.ToNetASCIIOutputStream; +import aria.apache.commons.net.io.Util; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Properties; +import java.util.Random; +import java.util.Set; + +import aria.apache.commons.net.MalformedServerReplyException; +import aria.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory; +import aria.apache.commons.net.ftp.parser.FTPFileEntryParserFactory; +import aria.apache.commons.net.ftp.parser.MLSxEntryParser; +import aria.apache.commons.net.io.CRLFLineReader; + +/** + * FTPClient encapsulates all the functionality necessary to store and + * retrieve files from an FTP server. This class takes care of all + * low level details of interacting with an FTP server and provides + * a convenient higher level interface. As with all classes derived + * from {@link SocketClient}, + * you must first connect to the server with + * {@link SocketClient#connect connect } + * before doing anything, and finally + * {@link SocketClient#disconnect disconnect } + * after you're completely finished interacting with the server. + * Then you need to check the FTP reply code to see if the connection + * was successful. For example: + *

+ *    FTPClient ftp = new FTPClient();
+ *    FTPClientConfig config = new FTPClientConfig();
+ *    config.setXXX(YYY); // change required options
+ *    // for example config.setServerTimeZoneId("Pacific/Pitcairn")
+ *    ftp.configure(config );
+ *    boolean error = false;
+ *    try {
+ *      int reply;
+ *      String server = "ftp.example.com";
+ *      ftp.connect(server);
+ *      System.out.println("Connected to " + server + ".");
+ *      System.out.print(ftp.getReplyString());
+ *
+ *      // After connection attempt, you should check the reply code to verify
+ *      // success.
+ *      reply = ftp.getReplyCode();
+ *
+ *      if(!FTPReply.isPositiveCompletion(reply)) {
+ *        ftp.disconnect();
+ *        System.err.println("FTP server refused connection.");
+ *        System.exit(1);
+ *      }
+ *      ... // transfer files
+ *      ftp.logout();
+ *    } catch(IOException e) {
+ *      error = true;
+ *      e.printStackTrace();
+ *    } finally {
+ *      if(ftp.isConnected()) {
+ *        try {
+ *          ftp.disconnect();
+ *        } catch(IOException ioe) {
+ *          // do nothing
+ *        }
+ *      }
+ *      System.exit(error ? 1 : 0);
+ *    }
+ * 
+ *

+ * Immediately after connecting is the only real time you need to check the + * reply code (because connect is of type void). The convention for all the + * FTP command methods in FTPClient is such that they either return a + * boolean value or some other value. + * The boolean methods return true on a successful completion reply from + * the FTP server and false on a reply resulting in an error condition or + * failure. The methods returning a value other than boolean return a value + * containing the higher level data produced by the FTP command, or null if a + * reply resulted in an error condition or failure. If you want to access + * the exact FTP reply code causing a success or failure, you must call + * {@link FTP#getReplyCode getReplyCode } after + * a success or failure. + *

+ * The default settings for FTPClient are for it to use + * FTP.ASCII_FILE_TYPE , + * FTP.NON_PRINT_TEXT_FORMAT , + * FTP.STREAM_TRANSFER_MODE , and + * FTP.FILE_STRUCTURE . The only file types directly supported + * are FTP.ASCII_FILE_TYPE and + * FTP.BINARY_FILE_TYPE . Because there are at least 4 + * different EBCDIC encodings, we have opted not to provide direct support + * for EBCDIC. To transfer EBCDIC and other unsupported file types you + * must create your own filter InputStreams and OutputStreams and wrap + * them around the streams returned or required by the FTPClient methods. + * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII} + * filter streams to provide transparent handling of ASCII files. We will + * consider incorporating EBCDIC support if there is enough demand. + *

+ * FTP.NON_PRINT_TEXT_FORMAT , + * FTP.STREAM_TRANSFER_MODE , and + * FTP.FILE_STRUCTURE are the only supported formats, + * transfer modes, and file structures. + *

+ * Because the handling of sockets on different platforms can differ + * significantly, the FTPClient automatically issues a new PORT (or EPRT) command + * prior to every transfer requiring that the server connect to the client's + * data port. This ensures identical problem-free behavior on Windows, Unix, + * and Macintosh platforms. Additionally, it relieves programmers from + * having to issue the PORT (or EPRT) command themselves and dealing with platform + * dependent issues. + *

+ * Additionally, for security purposes, all data connections to the + * client are verified to ensure that they originated from the intended + * party (host and port). If a data connection is initiated by an unexpected + * party, the command will close the socket and throw an IOException. You + * may disable this behavior with + * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}. + *

+ * You should keep in mind that the FTP server may choose to prematurely + * close a connection if the client has been idle for longer than a + * given time period (usually 900 seconds). The FTPClient class will detect a + * premature FTP server connection closing when it receives a + * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } + * response to a command. + * When that occurs, the FTP class method encountering that reply will throw + * an {@link FTPConnectionClosedException} + * . + * FTPConnectionClosedException + * is a subclass of IOException and therefore need not be + * caught separately, but if you are going to catch it separately, its + * catch block must appear before the more general IOException + * catch block. When you encounter an + * {@link FTPConnectionClosedException} + * , you must disconnect the connection with + * {@link #disconnect disconnect() } to properly clean up the + * system resources used by FTPClient. Before disconnecting, you may check the + * last reply code and text with + * {@link FTP#getReplyCode getReplyCode }, + * {@link FTP#getReplyString getReplyString }, + * and + * {@link FTP#getReplyStrings getReplyStrings}. + * You may avoid server disconnections while the client is idle by + * periodically sending NOOP commands to the server. + *

+ * Rather than list it separately for each method, we mention here that + * every method communicating with the server and throwing an IOException + * can also throw a + * {@link MalformedServerReplyException} + * , which is a subclass + * of IOException. A MalformedServerReplyException will be thrown when + * the reply received from the server deviates enough from the protocol + * specification that it cannot be interpreted in a useful manner despite + * attempts to be as lenient as possible. + *

+ * Listing API Examples + * Both paged and unpaged examples of directory listings are available, + * as follows: + *

+ * Unpaged (whole list) access, using a parser accessible by auto-detect: + *

+ *    FTPClient f = new FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = f.listFiles(directory);
+ * 
+ *

+ * Paged access, using a parser not accessible by auto-detect. The class + * defined in the first parameter of initateListParsing should be derived + * from org.apache.commons.net.FTPFileEntryParser: + *

+ *    FTPClient f = new FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPListParseEngine engine =
+ *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
+ *
+ *    while (engine.hasNext()) {
+ *       FTPFile[] files = engine.getNext(25);  // "page size" you want
+ *       //do whatever you want with these files, display them, etc.
+ *       //expensive FTPFile objects not created until needed.
+ *    }
+ * 
+ *

+ * Paged access, using a parser accessible by auto-detect: + *

+ *    FTPClient f = new FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPListParseEngine engine = f.initiateListParsing(directory);
+ *
+ *    while (engine.hasNext()) {
+ *       FTPFile[] files = engine.getNext(25);  // "page size" you want
+ *       //do whatever you want with these files, display them, etc.
+ *       //expensive FTPFile objects not created until needed.
+ *    }
+ * 
+ *

+ * For examples of using FTPClient on servers whose directory listings + *

    + *
  • use languages other than English
  • + *
  • use date formats other than the American English "standard" MM d yyyy
  • + *
  • are in different timezones and you need accurate timestamps for dependency checking + * as in Ant
  • + *
see {@link FTPClientConfig FTPClientConfig}. + *

+ * Control channel keep-alive feature: + *

+ * Please note: this does not apply to the methods where the user is responsible for writing + * or reading + * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} + * and the other xxxFileStream methods + *

+ * During file transfers, the data connection is busy, but the control connection is idle. + * FTP servers know that the control connection is in use, so won't close it through lack of + * activity, + * but it's a lot harder for network routers to know that the control and data connections are + * associated + * with each other. + * Some routers may treat the control connection as idle, and disconnect it if the transfer over the + * data + * connection takes longer than the allowable idle time for the router. + *
+ * One solution to this is to send a safe command (i.e. NOOP) over the control connection to reset + * the router's + * idle timer. This is enabled as follows: + *

+ *     ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
+ * 
+ * This will cause the file upload/download methods to send a NOOP approximately every 5 minutes. + * The following public methods support this: + *
    + *
  • {@link #retrieveFile(String, OutputStream)}
  • + *
  • {@link #appendFile(String, InputStream)}
  • + *
  • {@link #storeFile(String, InputStream)}
  • + *
  • {@link #storeUniqueFile(InputStream)}
  • + *
  • {@link #storeUniqueFileStream(String)}
  • + *
+ * This feature does not apply to the methods where the user is responsible for writing or reading + * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} + * and the other xxxFileStream methods. + * In such cases, the user is responsible for keeping the control connection alive if necessary. + *

+ * The implementation currently uses a {@link CopyStreamListener} which is passed to the + * {@link Util#copyStream(InputStream, OutputStream, int, long, CopyStreamListener, boolean)} + * method, so the timing is partially dependent on how long each block transfer takes. + * + * @see #FTP_SYSTEM_TYPE + * @see #SYSTEM_TYPE_PROPERTIES + * @see FTP + * @see FTPConnectionClosedException + * @see FTPFileEntryParser + * @see FTPFileEntryParserFactory + * @see DefaultFTPFileEntryParserFactory + * @see FTPClientConfig + * @see MalformedServerReplyException + */ +public class FTPClient extends FTP implements Configurable { + /** + * The system property ({@value}) which can be used to override the system type.
+ * If defined, the value will be used to create any automatically created parsers. + * + * @since 3.0 + */ + public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType"; + + /** + * The system property ({@value}) which can be used as the default system type.
+ * If defined, the value will be used if the SYST command fails. + * + * @since 3.1 + */ + public static final String FTP_SYSTEM_TYPE_DEFAULT = + "org.apache.commons.net.ftp.systemType.default"; + + /** + * The name of an optional systemType properties file ({@value}), which is loaded + * using {@link Class#getResourceAsStream(String)}.
+ * The entries are the systemType (as determined by {@link FTPClient#getSystemType}) + * and the values are the replacement type or parserClass, which is passed to + * {@link FTPFileEntryParserFactory#createFileEntryParser(String)}.
+ * For example: + *

+   * Plan 9=Unix
+   * OS410=OS400FTPEntryParser
+   * 
+ * + * @since 3.0 + */ + public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties"; + + /** + * A constant indicating the FTP session is expecting all transfers + * to occur between the client (local) and server and that the server + * should connect to the client's data port to initiate a data transfer. + * This is the default data connection mode when and FTPClient instance + * is created. + */ + public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0; + /** + * A constant indicating the FTP session is expecting all transfers + * to occur between two remote servers and that the server + * the client is connected to should connect to the other server's + * data port to initiate a data transfer. + */ + public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1; + /** + * A constant indicating the FTP session is expecting all transfers + * to occur between the client (local) and server and that the server + * is in passive mode, requiring the client to connect to the + * server's data port to initiate a transfer. + */ + public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2; + /** + * A constant indicating the FTP session is expecting all transfers + * to occur between two remote servers and that the server + * the client is connected to is in passive mode, requiring the other + * server to connect to the first server's data port to initiate a data + * transfer. + */ + public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3; + + private int __dataConnectionMode; + private int __dataTimeout; + private int __passivePort; + private String __passiveHost; + private final Random __random; + private int __activeMinPort; + private int __activeMaxPort; + private InetAddress __activeExternalHost; + private InetAddress __reportActiveExternalHost; + // overrides __activeExternalHost in EPRT/PORT commands + /** The address to bind to on passive connections, if necessary. */ + private InetAddress __passiveLocalHost; + + private int __fileType; + @SuppressWarnings("unused") // fields are written, but currently not read + private int __fileFormat; + @SuppressWarnings("unused") // field is written, but currently not read + private int __fileStructure; + @SuppressWarnings("unused") // field is written, but currently not read + private int __fileTransferMode; + private boolean __remoteVerificationEnabled; + private long __restartOffset; + private FTPFileEntryParserFactory __parserFactory; + private int __bufferSize; // buffersize for buffered data streams + private int __sendDataSocketBufferSize; + private int __receiveDataSocketBufferSize; + private boolean __listHiddenFiles; + private boolean __useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection + + // __systemName is a cached value that should not be referenced directly + // except when assigned in getSystemName and __initDefaults. + private String __systemName; + + // __entryParser is a cached value that should not be referenced directly + // except when assigned in listFiles(String, String) and __initDefaults. + private FTPFileEntryParser __entryParser; + + // Key used to create the parser; necessary to ensure that the parser type is not ignored + private String __entryParserKey; + + private FTPClientConfig __configuration; + + // Listener used by store/retrieve methods to handle keepalive + private CopyStreamListener __copyStreamListener; + + // How long to wait before sending another control keep-alive message + private long __controlKeepAliveTimeout; + + // How long to wait (ms) for keepalive message replies before continuing + // Most FTP servers don't seem to support concurrent control and data connection usage + private int __controlKeepAliveReplyTimeout = 1000; + + /** + * Enable or disable replacement of internal IP in passive mode. Default enabled + * using {code NatServerResolverImpl}. + */ + private HostnameResolver __passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); + + /** Pattern for PASV mode responses. Groups: (n,n,n,n),(n),(n) */ + private static final java.util.regex.Pattern __PARMS_PAT; + + static { + __PARMS_PAT = java.util.regex.Pattern.compile( + "(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})"); + } + + /** Controls the automatic server encoding detection (only UTF-8 supported). */ + private boolean __autodetectEncoding = false; + + /** Map of FEAT responses. If null, has not been initialised. */ + private HashMap> __featuresMap; + + private static class PropertiesSingleton { + + static final Properties PROPERTIES; + + static { + InputStream resourceAsStream = FTPClient.class.getResourceAsStream(SYSTEM_TYPE_PROPERTIES); + Properties p = null; + if (resourceAsStream != null) { + p = new Properties(); + try { + p.load(resourceAsStream); + } catch (IOException e) { + // Ignored + } finally { + try { + resourceAsStream.close(); + } catch (IOException e) { + // Ignored + } + } + } + PROPERTIES = p; + } + } + + private static Properties getOverrideProperties() { + return PropertiesSingleton.PROPERTIES; + } + + /** + * Default FTPClient constructor. Creates a new FTPClient instance + * with the data connection mode set to + * ACTIVE_LOCAL_DATA_CONNECTION_MODE , the file type + * set to FTP.ASCII_FILE_TYPE , the + * file format set to FTP.NON_PRINT_TEXT_FORMAT , + * the file structure set to FTP.FILE_STRUCTURE , and + * the transfer mode set to FTP.STREAM_TRANSFER_MODE . + *

+ * The list parsing auto-detect feature can be configured to use lenient future + * dates (short dates may be up to one day in the future) as follows: + *

+   * FTPClient ftp = new FTPClient();
+   * FTPClientConfig config = new FTPClientConfig();
+   * config.setLenientFutureDates(true);
+   * ftp.configure(config );
+   * 
+ */ + public FTPClient() { + __initDefaults(); + __dataTimeout = -1; + __remoteVerificationEnabled = true; + __parserFactory = new DefaultFTPFileEntryParserFactory(); + __configuration = null; + __listHiddenFiles = false; + __useEPSVwithIPv4 = false; + __random = new Random(); + __passiveLocalHost = null; + } + + private void __initDefaults() { + __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; + __passiveHost = null; + __passivePort = -1; + __activeExternalHost = null; + __reportActiveExternalHost = null; + __activeMinPort = 0; + __activeMaxPort = 0; + __fileType = FTP.ASCII_FILE_TYPE; + __fileStructure = FTP.FILE_STRUCTURE; + __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; + __fileTransferMode = FTP.STREAM_TRANSFER_MODE; + __restartOffset = 0; + __systemName = null; + __entryParser = null; + __entryParserKey = ""; + __featuresMap = null; + } + + /** + * Parse the pathname from a CWD reply. + *

+ * According to RFC959 (http://www.ietf.org/rfc/rfc959.txt), + * it should be the same as for MKD i.e. + * {@code 257""[commentary]} + * where any double-quotes in {@code } are doubled. + * Unlike MKD, the commentary is optional. + *

+ * However, see NET-442 for an exception. + * + * @return the pathname, without enclosing quotes, + * or the full string after the reply code and space if the syntax is invalid + * (i.e. enclosing quotes are missing or embedded quotes are not doubled) + */ + // package protected for access by test cases + static String __parsePathname(String reply) { + String param = reply.substring(REPLY_CODE_LEN + 1); + if (param.startsWith("\"")) { + StringBuilder sb = new StringBuilder(); + boolean quoteSeen = false; + // start after initial quote + for (int i = 1; i < param.length(); i++) { + char ch = param.charAt(i); + if (ch == '"') { + if (quoteSeen) { + sb.append(ch); + quoteSeen = false; + } else { + // don't output yet, in case doubled + quoteSeen = true; + } + } else { + if (quoteSeen) { // found lone trailing quote within string + return sb.toString(); + } + sb.append(ch); // just another character + } + } + if (quoteSeen) { // found lone trailing quote at end of string + return sb.toString(); + } + } + // malformed reply, return all after reply code and space + return param; + } + + /** + * @param reply the reply to parse + * @throws MalformedServerReplyException if the server reply does not match (n,n,n,n),(n),(n) + * @since 3.1 + */ + protected void _parsePassiveModeReply(String reply) throws MalformedServerReplyException { + java.util.regex.Matcher m = __PARMS_PAT.matcher(reply); + if (!m.find()) { + throw new MalformedServerReplyException( + "Could not parse passive host information.\nServer Reply: " + reply); + } + + __passiveHost = m.group(1).replace(',', '.'); // Fix up to look like IP address + + try { + int oct1 = Integer.parseInt(m.group(2)); + int oct2 = Integer.parseInt(m.group(3)); + __passivePort = (oct1 << 8) | oct2; + } catch (NumberFormatException e) { + throw new MalformedServerReplyException( + "Could not parse passive port information.\nServer Reply: " + reply); + } + + if (__passiveNatWorkaroundStrategy != null) { + try { + String passiveHost = __passiveNatWorkaroundStrategy.resolve(__passiveHost); + if (!__passiveHost.equals(passiveHost)) { + fireReplyReceived(0, "[Replacing PASV mode reply address " + + __passiveHost + + " with " + + passiveHost + + "]\n"); + __passiveHost = passiveHost; + } + } catch (UnknownHostException e) { // Should not happen as we are passing in an IP address + throw new MalformedServerReplyException( + "Could not parse passive host information.\nServer Reply: " + reply); + } + } + } + + protected void _parseExtendedPassiveModeReply(String reply) throws MalformedServerReplyException { + reply = reply.substring(reply.indexOf('(') + 1, reply.indexOf(')')).trim(); + + char delim1, delim2, delim3, delim4; + delim1 = reply.charAt(0); + delim2 = reply.charAt(1); + delim3 = reply.charAt(2); + delim4 = reply.charAt(reply.length() - 1); + + if (!(delim1 == delim2) || !(delim2 == delim3) || !(delim3 == delim4)) { + throw new MalformedServerReplyException( + "Could not parse extended passive host information.\nServer Reply: " + reply); + } + + int port; + try { + port = Integer.parseInt(reply.substring(3, reply.length() - 1)); + } catch (NumberFormatException e) { + throw new MalformedServerReplyException( + "Could not parse extended passive host information.\nServer Reply: " + reply); + } + + // in EPSV mode, the passive host address is implicit + __passiveHost = getRemoteAddress().getHostAddress(); + __passivePort = port; + } + + private boolean __storeFile(FTPCmd command, String remote, InputStream local) throws IOException { + return _storeFile(command.getCommand(), remote, local); + } + + /** + * @param command the command to send + * @param remote the remote file name + * @param local the local file name + * @return true if successful + * @throws IOException on error + * @since 3.1 + */ + protected boolean _storeFile(String command, String remote, InputStream local) + throws IOException { + Socket socket = _openDataConnection_(command, remote); + + if (socket == null) { + return false; + } + + final OutputStream output; + + if (__fileType == ASCII_FILE_TYPE) { + output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); + } else { + output = getBufferedOutputStream(socket.getOutputStream()); + } + + CSL csl = null; + if (__controlKeepAliveTimeout > 0) { + if (mListener != null) { + csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout, mListener); + } else { + csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); + } + } + + // Treat everything else as binary for now + try { + Util.copyStream(local, output, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, + __mergeListeners(csl), false); + } catch (IOException e) { + Util.closeQuietly(socket); // ignore close errors here + if (csl != null) { + csl.cleanUp(); // fetch any outstanding keepalive replies + } + throw e; + } + + output.close(); // ensure the file is fully written + socket.close(); // done writing the file + if (csl != null) { + csl.cleanUp(); // fetch any outstanding keepalive replies + } + // Get the transfer response + boolean ok = completePendingCommand(); + return ok; + } + + private OutputStream __storeFileStream(FTPCmd command, String remote) throws IOException { + return _storeFileStream(command.getCommand(), remote); + } + + /** + * @param command the command to send + * @param remote the remote file name + * @return the output stream to write to + * @throws IOException on error + * @since 3.1 + */ + protected OutputStream _storeFileStream(String command, String remote) throws IOException { + Socket socket = _openDataConnection_(command, remote); + + if (socket == null) { + return null; + } + + final OutputStream output; + if (__fileType == ASCII_FILE_TYPE) { + // We buffer ascii transfers because the buffering has to + // be interposed between ToNetASCIIOutputSream and the underlying + // socket output stream. We don't buffer binary transfers + // because we don't want to impose a buffering policy on the + // programmer if possible. Programmers can decide on their + // own if they want to wrap the SocketOutputStream we return + // for file types other than ASCII. + output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); + } else { + output = socket.getOutputStream(); + } + return new SocketOutputStream(socket, output); + } + + /** + * Establishes a data connection with the FTP server, returning + * a Socket for the connection if successful. If a restart + * offset has been set with {@link #setRestartOffset(long)}, + * a REST command is issued to the server with the offset as + * an argument before establishing the data connection. Active + * mode connections also cause a local PORT command to be issued. + * + * @param command The int representation of the FTP command to send. + * @param arg The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return A Socket corresponding to the established data connection. + * Null is returned if an FTP protocol error is reported at + * any point during the establishment and initialization of + * the connection. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @deprecated (3.3) Use {@link #_openDataConnection_(FTPCmd, String)} instead + */ + @Deprecated protected Socket _openDataConnection_(int command, String arg) throws IOException { + return _openDataConnection_(FTPCommand.getCommand(command), arg); + } + + /** + * Establishes a data connection with the FTP server, returning + * a Socket for the connection if successful. If a restart + * offset has been set with {@link #setRestartOffset(long)}, + * a REST command is issued to the server with the offset as + * an argument before establishing the data connection. Active + * mode connections also cause a local PORT command to be issued. + * + * @param command The int representation of the FTP command to send. + * @param arg The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return A Socket corresponding to the established data connection. + * Null is returned if an FTP protocol error is reported at + * any point during the establishment and initialization of + * the connection. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.3 + */ + protected Socket _openDataConnection_(FTPCmd command, String arg) throws IOException { + return _openDataConnection_(command.getCommand(), arg); + } + + /** + * Establishes a data connection with the FTP server, returning + * a Socket for the connection if successful. If a restart + * offset has been set with {@link #setRestartOffset(long)}, + * a REST command is issued to the server with the offset as + * an argument before establishing the data connection. Active + * mode connections also cause a local PORT command to be issued. + * + * @param command The text representation of the FTP command to send. + * @param arg The arguments to the FTP command. If this parameter is + * set to null, then the command is sent with no argument. + * @return A Socket corresponding to the established data connection. + * Null is returned if an FTP protocol error is reported at + * any point during the establishment and initialization of + * the connection. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.1 + */ + protected Socket _openDataConnection_(String command, String arg) throws IOException { + if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE + && __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { + return null; + } + + final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; + + Socket socket; + + if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE) { + // if no activePortRange was set (correctly) -> getActivePort() = 0 + // -> new ServerSocket(0) -> bind to any free local port + ServerSocket server = + _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress()); + + try { + // Try EPRT only if remote server is over IPv6, if not use PORT, + // because EPRT has no advantage over PORT on IPv4. + // It could even have the disadvantage, + // that EPRT will make the data connection fail, because + // today's intelligent NAT Firewalls are able to + // substitute IP addresses in the PORT command, + // but might not be able to recognize the EPRT command. + if (isInet6Address) { + if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) { + return null; + } + } else { + if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) { + return null; + } + } + + if ((__restartOffset > 0) && !restart(__restartOffset)) { + return null; + } + + if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { + return null; + } + + // For now, let's just use the data timeout value for waiting for + // the data connection. It may be desirable to let this be a + // separately configurable value. In any case, we really want + // to allow preventing the accept from blocking indefinitely. + if (__dataTimeout >= 0) { + server.setSoTimeout(__dataTimeout); + } + socket = server.accept(); + + // Ensure the timeout is set before any commands are issued on the new socket + if (__dataTimeout >= 0) { + socket.setSoTimeout(__dataTimeout); + } + if (__receiveDataSocketBufferSize > 0) { + socket.setReceiveBufferSize(__receiveDataSocketBufferSize); + } + if (__sendDataSocketBufferSize > 0) { + socket.setSendBufferSize(__sendDataSocketBufferSize); + } + } finally { + server.close(); + } + } else { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE + + // Try EPSV command first on IPv6 - and IPv4 if enabled. + // When using IPv4 with NAT it has the advantage + // to work with more rare configurations. + // E.g. if FTP server has a static PASV address (external network) + // and the client is coming from another internal network. + // In that case the data connection after PASV command would fail, + // while EPSV would make the client succeed by taking just the port. + boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; + if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { + _parseExtendedPassiveModeReply(_replyLines.get(0)); + } else { + if (isInet6Address) { + return null; // Must use EPSV for IPV6 + } + // If EPSV failed on IPV4, revert to PASV + if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { + return null; + } + _parsePassiveModeReply(_replyLines.get(0)); + } + + socket = _socketFactory_.createSocket(); + if (__receiveDataSocketBufferSize > 0) { + socket.setReceiveBufferSize(__receiveDataSocketBufferSize); + } + if (__sendDataSocketBufferSize > 0) { + socket.setSendBufferSize(__sendDataSocketBufferSize); + } + if (__passiveLocalHost != null) { + socket.bind(new InetSocketAddress(__passiveLocalHost, 0)); + } + + // For now, let's just use the data timeout value for waiting for + // the data connection. It may be desirable to let this be a + // separately configurable value. In any case, we really want + // to allow preventing the accept from blocking indefinitely. + if (__dataTimeout >= 0) { + socket.setSoTimeout(__dataTimeout); + } + + socket.connect(new InetSocketAddress(__passiveHost, __passivePort), connectTimeout); + if ((__restartOffset > 0) && !restart(__restartOffset)) { + socket.close(); + return null; + } + + if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { + socket.close(); + return null; + } + } + + if (__remoteVerificationEnabled && !verifyRemote(socket)) { + socket.close(); + + throw new IOException("Host attempting data connection " + + socket.getInetAddress().getHostAddress() + + " is not same as server " + + getRemoteAddress().getHostAddress()); + } + + return socket; + } + + @Override protected void _connectAction_() throws IOException { + _connectAction_(null); + } + + /** + * @param socketIsReader the reader to reuse (if non-null) + * @throws IOException on error + * @since 3.4 + */ + @Override protected void _connectAction_(Reader socketIsReader) throws IOException { + super._connectAction_(socketIsReader); // sets up _input_ and _output_ + __initDefaults(); + // must be after super._connectAction_(), because otherwise we get an + // Exception claiming we're not connected + if (__autodetectEncoding) { + ArrayList oldReplyLines = new ArrayList(_replyLines); + int oldReplyCode = _replyCode; + if (hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default + { + setControlEncoding("UTF-8"); + _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); + _controlOutput_ = + new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); + } + // restore the original reply (server greeting) + _replyLines.clear(); + _replyLines.addAll(oldReplyLines); + _replyCode = oldReplyCode; + _newReplyString = true; + } + } + + /** + * Sets the timeout in milliseconds to use when reading from the + * data connection. This timeout will be set immediately after + * opening the data connection, provided that the value is ≥ 0. + *

+ * Note: the timeout will also be applied when calling accept() + * whilst establishing an active local data connection. + * + * @param timeout The default timeout in milliseconds that is used when + * opening a data connection socket. The value 0 means an infinite timeout. + */ + public void setDataTimeout(int timeout) { + __dataTimeout = timeout; + } + + /** + * set the factory used for parser creation to the supplied factory object. + * + * @param parserFactory factory object used to create FTPFileEntryParsers + * @see FTPFileEntryParserFactory + * @see DefaultFTPFileEntryParserFactory + */ + public void setParserFactory(FTPFileEntryParserFactory parserFactory) { + __parserFactory = parserFactory; + } + + /** + * Closes the connection to the FTP server and restores + * connection parameters to the default values. + * + * @throws IOException If an error occurs while disconnecting. + */ + @Override public void disconnect() throws IOException { + super.disconnect(); + __initDefaults(); + } + + /** + * Enable or disable verification that the remote host taking part + * of a data connection is the same as the host to which the control + * connection is attached. The default is for verification to be + * enabled. You may set this value at any time, whether the + * FTPClient is currently connected or not. + * + * @param enable True to enable verification, false to disable verification. + */ + public void setRemoteVerificationEnabled(boolean enable) { + __remoteVerificationEnabled = enable; + } + + /** + * Return whether or not verification of the remote host participating + * in data connections is enabled. The default behavior is for + * verification to be enabled. + * + * @return True if verification is enabled, false if not. + */ + public boolean isRemoteVerificationEnabled() { + return __remoteVerificationEnabled; + } + + /** + * Login to the FTP server using the provided username and password. + * + * @param username The username to login under. + * @param password The password to use. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean login(String username, String password) throws IOException { + + user(username); + + if (FTPReply.isPositiveCompletion(_replyCode)) { + return true; + } + + // If we get here, we either have an error code, or an intermmediate + // reply requesting password. + if (!FTPReply.isPositiveIntermediate(_replyCode)) { + return false; + } + + return FTPReply.isPositiveCompletion(pass(password)); + } + + /** + * Login to the FTP server using the provided username, password, + * and account. If no account is required by the server, only + * the username and password, the account information is not used. + * + * @param username The username to login under. + * @param password The password to use. + * @param account The account to use. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean login(String username, String password, String account) throws IOException { + user(username); + + if (FTPReply.isPositiveCompletion(_replyCode)) { + return true; + } + + // If we get here, we either have an error code, or an intermmediate + // reply requesting password. + if (!FTPReply.isPositiveIntermediate(_replyCode)) { + return false; + } + + pass(password); + + if (FTPReply.isPositiveCompletion(_replyCode)) { + return true; + } + + if (!FTPReply.isPositiveIntermediate(_replyCode)) { + return false; + } + + return FTPReply.isPositiveCompletion(acct(account)); + } + + /** + * Logout of the FTP server by sending the QUIT command. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean logout() throws IOException { + return FTPReply.isPositiveCompletion(quit()); + } + + /** + * Change the current working directory of the FTP session. + * + * @param pathname The new current working directory. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean changeWorkingDirectory(String pathname) throws IOException { + return FTPReply.isPositiveCompletion(cwd(pathname)); + } + + /** + * Change to the parent directory of the current working directory. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean changeToParentDirectory() throws IOException { + return FTPReply.isPositiveCompletion(cdup()); + } + + /** + * Issue the FTP SMNT command. + * + * @param pathname The pathname to mount. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean structureMount(String pathname) throws IOException { + return FTPReply.isPositiveCompletion(smnt(pathname)); + } + + /** + * Reinitialize the FTP session. Not all FTP servers support this + * command, which issues the FTP REIN command. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.4 (made public) + */ + public boolean reinitialize() throws IOException { + rein(); + + if (FTPReply.isPositiveCompletion(_replyCode) || (FTPReply.isPositivePreliminary(_replyCode) + && FTPReply.isPositiveCompletion(getReply()))) { + + __initDefaults(); + + return true; + } + + return false; + } + + /** + * Set the current data connection mode to + * ACTIVE_LOCAL_DATA_CONNECTION_MODE. No communication + * with the FTP server is conducted, but this causes all future data + * transfers to require the FTP server to connect to the client's + * data port. Additionally, to accommodate differences between socket + * implementations on different platforms, this method causes the + * client to issue a PORT command before every data transfer. + */ + public void enterLocalActiveMode() { + __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; + __passiveHost = null; + __passivePort = -1; + } + + /** + * Set the current data connection mode to + * PASSIVE_LOCAL_DATA_CONNECTION_MODE . Use this + * method only for data transfers between the client and server. + * This method causes a PASV (or EPSV) command to be issued to the server + * before the opening of every data connection, telling the server to + * open a data port to which the client will connect to conduct + * data transfers. The FTPClient will stay in + * PASSIVE_LOCAL_DATA_CONNECTION_MODE until the + * mode is changed by calling some other method such as + * {@link #enterLocalActiveMode enterLocalActiveMode() } + *

+ * N.B. currently calling any connect method will reset the mode to + * ACTIVE_LOCAL_DATA_CONNECTION_MODE. + */ + public void enterLocalPassiveMode() { + __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE; + // These will be set when just before a data connection is opened + // in _openDataConnection_() + __passiveHost = null; + __passivePort = -1; + } + + /** + * Set the current data connection mode to + * ACTIVE_REMOTE_DATA_CONNECTION . Use this method only + * for server to server data transfers. This method issues a PORT + * command to the server, indicating the other server and port to which + * it should connect for data transfers. You must call this method + * before EVERY server to server transfer attempt. The FTPClient will + * NOT automatically continue to issue PORT commands. You also + * must remember to call + * {@link #enterLocalActiveMode enterLocalActiveMode() } if you + * wish to return to the normal data connection mode. + * + * @param host The passive mode server accepting connections for data + * transfers. + * @param port The passive mode server's data port. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean enterRemoteActiveMode(InetAddress host, int port) throws IOException { + if (FTPReply.isPositiveCompletion(port(host, port))) { + __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE; + __passiveHost = null; + __passivePort = -1; + return true; + } + return false; + } + + /** + * Set the current data connection mode to + * PASSIVE_REMOTE_DATA_CONNECTION_MODE . Use this + * method only for server to server data transfers. + * This method issues a PASV command to the server, telling it to + * open a data port to which the active server will connect to conduct + * data transfers. You must call this method + * before EVERY server to server transfer attempt. The FTPClient will + * NOT automatically continue to issue PASV commands. You also + * must remember to call + * {@link #enterLocalActiveMode enterLocalActiveMode() } if you + * wish to return to the normal data connection mode. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean enterRemotePassiveMode() throws IOException { + if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { + return false; + } + + __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE; + _parsePassiveModeReply(_replyLines.get(0)); + + return true; + } + + /** + * Returns the hostname or IP address (in the form of a string) returned + * by the server when entering passive mode. If not in passive mode, + * returns null. This method only returns a valid value AFTER a + * data connection has been opened after a call to + * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. + * This is because FTPClient sends a PASV command to the server only + * just before opening a data connection, and not when you call + * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. + * + * @return The passive host name if in passive mode, otherwise null. + */ + public String getPassiveHost() { + return __passiveHost; + } + + /** + * If in passive mode, returns the data port of the passive host. + * This method only returns a valid value AFTER a + * data connection has been opened after a call to + * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. + * This is because FTPClient sends a PASV command to the server only + * just before opening a data connection, and not when you call + * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. + * + * @return The data port of the passive server. If not in passive + * mode, undefined. + */ + public int getPassivePort() { + return __passivePort; + } + + /** + * Returns the current data connection mode (one of the + * _DATA_CONNECTION_MODE constants. + * + * @return The current data connection mode (one of the + * _DATA_CONNECTION_MODE constants. + */ + public int getDataConnectionMode() { + return __dataConnectionMode; + } + + /** + * Get the client port for active mode. + * + * @return The client port for active mode. + */ + private int getActivePort() { + if (__activeMinPort > 0 && __activeMaxPort >= __activeMinPort) { + if (__activeMaxPort == __activeMinPort) { + return __activeMaxPort; + } + // Get a random port between the min and max port range + return __random.nextInt(__activeMaxPort - __activeMinPort + 1) + __activeMinPort; + } else { + // default port + return 0; + } + } + + /** + * Get the host address for active mode; allows the local address to be overridden. + * + * @return __activeExternalHost if non-null, else getLocalAddress() + * @see #setActiveExternalIPAddress(String) + */ + private InetAddress getHostAddress() { + if (__activeExternalHost != null) { + return __activeExternalHost; + } else { + // default local address + return getLocalAddress(); + } + } + + /** + * Get the reported host address for active mode EPRT/PORT commands; + * allows override of {@link #getHostAddress()}. + * + * Useful for FTP Client behind Firewall NAT. + * + * @return __reportActiveExternalHost if non-null, else getHostAddress(); + */ + private InetAddress getReportHostAddress() { + if (__reportActiveExternalHost != null) { + return __reportActiveExternalHost; + } else { + return getHostAddress(); + } + } + + /** + * Set the client side port range in active mode. + * + * @param minPort The lowest available port (inclusive). + * @param maxPort The highest available port (inclusive). + * @since 2.2 + */ + public void setActivePortRange(int minPort, int maxPort) { + this.__activeMinPort = minPort; + this.__activeMaxPort = maxPort; + } + + /** + * Set the external IP address in active mode. + * Useful when there are multiple network cards. + * + * @param ipAddress The external IP address of this machine. + * @throws UnknownHostException if the ipAddress cannot be resolved + * @since 2.2 + */ + public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException { + this.__activeExternalHost = InetAddress.getByName(ipAddress); + } + + /** + * Set the local IP address to use in passive mode. + * Useful when there are multiple network cards. + * + * @param ipAddress The local IP address of this machine. + * @throws UnknownHostException if the ipAddress cannot be resolved + */ + public void setPassiveLocalIPAddress(String ipAddress) throws UnknownHostException { + this.__passiveLocalHost = InetAddress.getByName(ipAddress); + } + + /** + * Set the local IP address to use in passive mode. + * Useful when there are multiple network cards. + * + * @param inetAddress The local IP address of this machine. + */ + public void setPassiveLocalIPAddress(InetAddress inetAddress) { + this.__passiveLocalHost = inetAddress; + } + + /** + * Set the local IP address in passive mode. + * Useful when there are multiple network cards. + * + * @return The local IP address in passive mode. + */ + public InetAddress getPassiveLocalIPAddress() { + return this.__passiveLocalHost; + } + + /** + * Set the external IP address to report in EPRT/PORT commands in active mode. + * Useful when there are multiple network cards. + * + * @param ipAddress The external IP address of this machine. + * @throws UnknownHostException if the ipAddress cannot be resolved + * @see #getReportHostAddress() + * @since 3.1 + */ + public void setReportActiveExternalIPAddress(String ipAddress) throws UnknownHostException { + this.__reportActiveExternalHost = InetAddress.getByName(ipAddress); + } + + /** + * Sets the file type to be transferred. This should be one of + * FTP.ASCII_FILE_TYPE , FTP.BINARY_FILE_TYPE, + * etc. The file type only needs to be set when you want to change the + * type. After changing it, the new type stays in effect until you change + * it again. The default file type is FTP.ASCII_FILE_TYPE + * if this method is never called. + *
+ * The server default is supposed to be ASCII (see RFC 959), however many + * ftp servers default to BINARY. To ensure correct operation with all servers, + * always specify the appropriate file type after connecting to the server. + *
+ *

+ * N.B. currently calling any connect method will reset the type to + * FTP.ASCII_FILE_TYPE. + * + * @param fileType The _FILE_TYPE constant indcating the + * type of file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean setFileType(int fileType) throws IOException { + if (FTPReply.isPositiveCompletion(type(fileType))) { + __fileType = fileType; + __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; + return true; + } + return false; + } + + /** + * Sets the file type to be transferred and the format. The type should be + * one of FTP.ASCII_FILE_TYPE , + * FTP.BINARY_FILE_TYPE , etc. The file type only needs to + * be set when you want to change the type. After changing it, the new + * type stays in effect until you change it again. The default file type + * is FTP.ASCII_FILE_TYPE if this method is never called. + *
+ * The server default is supposed to be ASCII (see RFC 959), however many + * ftp servers default to BINARY. To ensure correct operation with all servers, + * always specify the appropriate file type after connecting to the server. + *
+ * The format should be one of the FTP class TEXT_FORMAT + * constants, or if the type is FTP.LOCAL_FILE_TYPE , the + * format should be the byte size for that type. The default format + * is FTP.NON_PRINT_TEXT_FORMAT if this method is never + * called. + *

+ * N.B. currently calling any connect method will reset the type to + * FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT. + * + * @param fileType The _FILE_TYPE constant indcating the + * type of file. + * @param formatOrByteSize The format of the file (one of the + * _FORMAT constants. In the case of + * LOCAL_FILE_TYPE, the byte size. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean setFileType(int fileType, int formatOrByteSize) throws IOException { + if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize))) { + __fileType = fileType; + __fileFormat = formatOrByteSize; + return true; + } + return false; + } + + /** + * Sets the file structure. The default structure is + * FTP.FILE_STRUCTURE if this method is never called + * or if a connect method is called. + * + * @param structure The structure of the file (one of the FTP class + * _STRUCTURE constants). + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean setFileStructure(int structure) throws IOException { + if (FTPReply.isPositiveCompletion(stru(structure))) { + __fileStructure = structure; + return true; + } + return false; + } + + /** + * Sets the transfer mode. The default transfer mode + * FTP.STREAM_TRANSFER_MODE if this method is never called + * or if a connect method is called. + * + * @param mode The new transfer mode to use (one of the FTP class + * _TRANSFER_MODE constants). + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean setFileTransferMode(int mode) throws IOException { + if (FTPReply.isPositiveCompletion(mode(mode))) { + __fileTransferMode = mode; + return true; + } + return false; + } + + /** + * Initiate a server to server file transfer. This method tells the + * server to which the client is connected to retrieve a given file from + * the other server. + * + * @param filename The name of the file to retrieve. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean remoteRetrieve(String filename) throws IOException { + if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE + || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { + return FTPReply.isPositivePreliminary(retr(filename)); + } + return false; + } + + /** + * Initiate a server to server file transfer. This method tells the + * server to which the client is connected to store a file on + * the other server using the given filename. The other server must + * have had a remoteRetrieve issued to it by another + * FTPClient. + * + * @param filename The name to call the file that is to be stored. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean remoteStore(String filename) throws IOException { + if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE + || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { + return FTPReply.isPositivePreliminary(stor(filename)); + } + return false; + } + + /** + * Initiate a server to server file transfer. This method tells the + * server to which the client is connected to store a file on + * the other server using a unique filename based on the given filename. + * The other server must have had a remoteRetrieve issued + * to it by another FTPClient. + * + * @param filename The name on which to base the filename of the file + * that is to be stored. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean remoteStoreUnique(String filename) throws IOException { + if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE + || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { + return FTPReply.isPositivePreliminary(stou(filename)); + } + return false; + } + + /** + * Initiate a server to server file transfer. This method tells the + * server to which the client is connected to store a file on + * the other server using a unique filename. + * The other server must have had a remoteRetrieve issued + * to it by another FTPClient. Many FTP servers require that a base + * filename be given from which the unique filename can be derived. For + * those servers use the other version of remoteStoreUnique + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean remoteStoreUnique() throws IOException { + if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE + || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { + return FTPReply.isPositivePreliminary(stou()); + } + return false; + } + + // For server to server transfers + + /** + * Initiate a server to server file transfer. This method tells the + * server to which the client is connected to append to a given file on + * the other server. The other server must have had a + * remoteRetrieve issued to it by another FTPClient. + * + * @param filename The name of the file to be appended to, or if the + * file does not exist, the name to call the file being stored. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean remoteAppend(String filename) throws IOException { + if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE + || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { + return FTPReply.isPositivePreliminary(appe(filename)); + } + return false; + } + + /** + * There are a few FTPClient methods that do not complete the + * entire sequence of FTP commands to complete a transaction. These + * commands require some action by the programmer after the reception + * of a positive intermediate command. After the programmer's code + * completes its actions, it must call this method to receive + * the completion reply from the server and verify the success of the + * entire transaction. + *

+ * For example, + *

+   * InputStream input;
+   * OutputStream output;
+   * input  = new FileInputStream("foobaz.txt");
+   * output = ftp.storeFileStream("foobar.txt")
+   * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
+   *     input.close();
+   *     output.close();
+   *     ftp.logout();
+   *     ftp.disconnect();
+   *     System.err.println("File transfer failed.");
+   *     System.exit(1);
+   * }
+   * Util.copyStream(input, output);
+   * input.close();
+   * output.close();
+   * // Must call completePendingCommand() to finish command.
+   * if(!ftp.completePendingCommand()) {
+   *     ftp.logout();
+   *     ftp.disconnect();
+   *     System.err.println("File transfer failed.");
+   *     System.exit(1);
+   * }
+   * 
+ * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean completePendingCommand() throws IOException { + return FTPReply.isPositiveCompletion(getReply()); + } + + /** + * Retrieves a named file from the server and writes it to the given + * OutputStream. This method does NOT close the given OutputStream. + * If the current file type is ASCII, line separators in the file are + * converted to the local representation. + *

+ * Note: if you have used {@link #setRestartOffset(long)}, + * the file data will start from the selected offset. + * + * @param remote The name of the remote file. + * @param local The local OutputStream to which to write the file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws CopyStreamException If an I/O error occurs while actually + * transferring the file. The CopyStreamException allows you to + * determine the number of bytes transferred and the IOException + * causing the error. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean retrieveFile(String remote, OutputStream local) throws IOException { + return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local); + } + + /** + * @param command the command to get + * @param remote the remote file name + * @param local the local file name + * @return true if successful + * @throws IOException on error + * @since 3.1 + */ + protected boolean _retrieveFile(String command, String remote, OutputStream local) + throws IOException { + Socket socket = _openDataConnection_(command, remote); + + if (socket == null) { + return false; + } + + final InputStream input; + if (__fileType == ASCII_FILE_TYPE) { + input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); + } else { + input = getBufferedInputStream(socket.getInputStream()); + } + + CSL csl = null; + if (__controlKeepAliveTimeout > 0) { + csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); + } + + // Treat everything else as binary for now + try { + Util.copyStream(input, local, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, + __mergeListeners(csl), false); + } finally { + Util.closeQuietly(input); + Util.closeQuietly(socket); + if (csl != null) { + csl.cleanUp(); // fetch any outstanding keepalive replies + } + } + + // Get the transfer response + boolean ok = completePendingCommand(); + return ok; + } + + /** + * Returns an InputStream from which a named file from the server + * can be read. If the current file type is ASCII, the returned + * InputStream will convert line separators in the file to + * the local representation. You must close the InputStream when you + * finish reading from it. The InputStream itself will take care of + * closing the parent data connection socket upon being closed. + *

+ * To finalize the file transfer you must call + * {@link #completePendingCommand completePendingCommand } and + * check its return value to verify success. + * If this is not done, subsequent commands may behave unexpectedly. + *

+ * Note: if you have used {@link #setRestartOffset(long)}, + * the file data will start from the selected offset. + * + * @param remote The name of the remote file. + * @return An InputStream from which the remote file can be read. If + * the data connection cannot be opened (e.g., the file does not + * exist), null is returned (in which case you may check the reply + * code to determine the exact reason for failure). + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public InputStream retrieveFileStream(String remote) throws IOException { + return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote); + } + + /** + * @param command the command to send + * @param remote the remote file name + * @return the stream from which to read the file + * @throws IOException on error + * @since 3.1 + */ + protected InputStream _retrieveFileStream(String command, String remote) throws IOException { + Socket socket = _openDataConnection_(command, remote); + + if (socket == null) { + return null; + } + + final InputStream input; + if (__fileType == ASCII_FILE_TYPE) { + // We buffer ascii transfers because the buffering has to + // be interposed between FromNetASCIIOutputSream and the underlying + // socket input stream. We don't buffer binary transfers + // because we don't want to impose a buffering policy on the + // programmer if possible. Programmers can decide on their + // own if they want to wrap the SocketInputStream we return + // for file types other than ASCII. + input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); + } else { + input = socket.getInputStream(); + } + return new SocketInputStream(socket, input); + } + + /** + * Stores a file on the server using the given name and taking input + * from the given InputStream. This method does NOT close the given + * InputStream. If the current file type is ASCII, line separators in + * the file are transparently converted to the NETASCII format (i.e., + * you should not attempt to create a special InputStream to do this). + * + * @param remote The name to give the remote file. + * @param local The local InputStream from which to read the file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws CopyStreamException If an I/O error occurs while actually + * transferring the file. The CopyStreamException allows you to + * determine the number of bytes transferred and the IOException + * causing the error. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean storeFile(String remote, InputStream local) throws IOException { + return __storeFile(FTPCmd.STOR, remote, local); + } + + OnFtpInputStreamListener mListener; + + /** + * 含有事件的Ftp上传文件 + * + * @throws IOException + */ + public boolean storeFile(String remote, InputStream local, OnFtpInputStreamListener listener) + throws IOException { + mListener = listener; + return __storeFile(FTPCmd.STOR, remote, local); + } + + /** + * Returns an OutputStream through which data can be written to store + * a file on the server using the given name. If the current file type + * is ASCII, the returned OutputStream will convert line separators in + * the file to the NETASCII format (i.e., you should not attempt to + * create a special OutputStream to do this). You must close the + * OutputStream when you finish writing to it. The OutputStream itself + * will take care of closing the parent data connection socket upon being + * closed. + *

+ * To finalize the file transfer you must call + * {@link #completePendingCommand completePendingCommand } and + * check its return value to verify success. + * If this is not done, subsequent commands may behave unexpectedly. + * + * @param remote The name to give the remote file. + * @return An OutputStream through which the remote file can be written. If + * the data connection cannot be opened (e.g., the file does not + * exist), null is returned (in which case you may check the reply + * code to determine the exact reason for failure). + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public OutputStream storeFileStream(String remote) throws IOException { + return __storeFileStream(FTPCmd.STOR, remote); + } + + /** + * Appends to a file on the server with the given name, taking input + * from the given InputStream. This method does NOT close the given + * InputStream. If the current file type is ASCII, line separators in + * the file are transparently converted to the NETASCII format (i.e., + * you should not attempt to create a special InputStream to do this). + * + * @param remote The name of the remote file. + * @param local The local InputStream from which to read the data to + * be appended to the remote file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws CopyStreamException If an I/O error occurs while actually + * transferring the file. The CopyStreamException allows you to + * determine the number of bytes transferred and the IOException + * causing the error. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean appendFile(String remote, InputStream local) throws IOException { + return __storeFile(FTPCmd.APPE, remote, local); + } + + /** + * Returns an OutputStream through which data can be written to append + * to a file on the server with the given name. If the current file type + * is ASCII, the returned OutputStream will convert line separators in + * the file to the NETASCII format (i.e., you should not attempt to + * create a special OutputStream to do this). You must close the + * OutputStream when you finish writing to it. The OutputStream itself + * will take care of closing the parent data connection socket upon being + * closed. + *

+ * To finalize the file transfer you must call + * {@link #completePendingCommand completePendingCommand } and + * check its return value to verify success. + * If this is not done, subsequent commands may behave unexpectedly. + * + * @param remote The name of the remote file. + * @return An OutputStream through which the remote file can be appended. + * If the data connection cannot be opened (e.g., the file does not + * exist), null is returned (in which case you may check the reply + * code to determine the exact reason for failure). + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public OutputStream appendFileStream(String remote) throws IOException { + return __storeFileStream(FTPCmd.APPE, remote); + } + + /** + * Stores a file on the server using a unique name derived from the + * given name and taking input + * from the given InputStream. This method does NOT close the given + * InputStream. If the current file type is ASCII, line separators in + * the file are transparently converted to the NETASCII format (i.e., + * you should not attempt to create a special InputStream to do this). + * + * @param remote The name on which to base the unique name given to + * the remote file. + * @param local The local InputStream from which to read the file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws CopyStreamException If an I/O error occurs while actually + * transferring the file. The CopyStreamException allows you to + * determine the number of bytes transferred and the IOException + * causing the error. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean storeUniqueFile(String remote, InputStream local) throws IOException { + return __storeFile(FTPCmd.STOU, remote, local); + } + + /** + * Returns an OutputStream through which data can be written to store + * a file on the server using a unique name derived from the given name. + * If the current file type + * is ASCII, the returned OutputStream will convert line separators in + * the file to the NETASCII format (i.e., you should not attempt to + * create a special OutputStream to do this). You must close the + * OutputStream when you finish writing to it. The OutputStream itself + * will take care of closing the parent data connection socket upon being + * closed. + *

+ * To finalize the file transfer you must call + * {@link #completePendingCommand completePendingCommand } and + * check its return value to verify success. + * If this is not done, subsequent commands may behave unexpectedly. + * + * @param remote The name on which to base the unique name given to + * the remote file. + * @return An OutputStream through which the remote file can be written. If + * the data connection cannot be opened (e.g., the file does not + * exist), null is returned (in which case you may check the reply + * code to determine the exact reason for failure). + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public OutputStream storeUniqueFileStream(String remote) throws IOException { + return __storeFileStream(FTPCmd.STOU, remote); + } + + /** + * Stores a file on the server using a unique name assigned by the + * server and taking input from the given InputStream. This method does + * NOT close the given + * InputStream. If the current file type is ASCII, line separators in + * the file are transparently converted to the NETASCII format (i.e., + * you should not attempt to create a special InputStream to do this). + * + * @param local The local InputStream from which to read the file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws CopyStreamException If an I/O error occurs while actually + * transferring the file. The CopyStreamException allows you to + * determine the number of bytes transferred and the IOException + * causing the error. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean storeUniqueFile(InputStream local) throws IOException { + return __storeFile(FTPCmd.STOU, null, local); + } + + /** + * Returns an OutputStream through which data can be written to store + * a file on the server using a unique name assigned by the server. + * If the current file type + * is ASCII, the returned OutputStream will convert line separators in + * the file to the NETASCII format (i.e., you should not attempt to + * create a special OutputStream to do this). You must close the + * OutputStream when you finish writing to it. The OutputStream itself + * will take care of closing the parent data connection socket upon being + * closed. + *

+ * To finalize the file transfer you must call + * {@link #completePendingCommand completePendingCommand } and + * check its return value to verify success. + * If this is not done, subsequent commands may behave unexpectedly. + * + * @return An OutputStream through which the remote file can be written. If + * the data connection cannot be opened (e.g., the file does not + * exist), null is returned (in which case you may check the reply + * code to determine the exact reason for failure). + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public OutputStream storeUniqueFileStream() throws IOException { + return __storeFileStream(FTPCmd.STOU, null); + } + + /** + * Reserve a number of bytes on the server for the next file transfer. + * + * @param bytes The number of bytes which the server should allocate. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean allocate(int bytes) throws IOException { + return FTPReply.isPositiveCompletion(allo(bytes)); + } + + /** + * Query the server for supported features. The server may reply with a list of server-supported + * exensions. + * For example, a typical client-server interaction might be (from RFC 2389): + *

+   * C> feat
+   * S> 211-Extensions supported:
+   * S>  MLST size*;create;modify*;perm;media-type
+   * S>  SIZE
+   * S>  COMPRESSION
+   * S>  MDTM
+   * S> 211 END
+   * 
+ * + * @return True if successfully completed, false if not. + * @throws IOException on error + * @see http://www.faqs.org/rfcs/rfc2389.html + * @since 2.2 + */ + public boolean features() throws IOException { + return FTPReply.isPositiveCompletion(feat()); + } + + /** + * Query the server for a supported feature, and returns its values (if any). + * Caches the parsed response to avoid resending the command repeatedly. + * + * @param feature the feature to check + * @return if the feature is present, returns the feature values (empty array if none) + * Returns {@code null} if the feature is not found or the command failed. + * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. + * @throws IOException on error + * @since 3.0 + */ + public String[] featureValues(String feature) throws IOException { + if (!initFeatureMap()) { + return null; + } + Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); + if (entries != null) { + return entries.toArray(new String[entries.size()]); + } + return null; + } + + /** + * Query the server for a supported feature, and returns the its value (if any). + * Caches the parsed response to avoid resending the command repeatedly. + * + * @param feature the feature to check + * @return if the feature is present, returns the feature value or the empty string + * if the feature exists but has no value. + * Returns {@code null} if the feature is not found or the command failed. + * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. + * @throws IOException on error + * @since 3.0 + */ + public String featureValue(String feature) throws IOException { + String[] values = featureValues(feature); + if (values != null) { + return values[0]; + } + return null; + } + + /** + * Query the server for a supported feature. + * Caches the parsed response to avoid resending the command repeatedly. + * + * @param feature the name of the feature; it is converted to upper case. + * @return {@code true} if the feature is present, {@code false} if the feature is not present + * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link + * #getReplyString()} + * if it is necessary to distinguish these cases. + * @throws IOException on error + * @since 3.0 + */ + public boolean hasFeature(String feature) throws IOException { + if (!initFeatureMap()) { + return false; + } + return __featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH)); + } + + /** + * Query the server for a supported feature with particular value, + * for example "AUTH SSL" or "AUTH TLS". + * Caches the parsed response to avoid resending the command repeatedly. + * + * @param feature the name of the feature; it is converted to upper case. + * @param value the value to find. + * @return {@code true} if the feature is present, {@code false} if the feature is not present + * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link + * #getReplyString()} + * if it is necessary to distinguish these cases. + * @throws IOException on error + * @since 3.0 + */ + public boolean hasFeature(String feature, String value) throws IOException { + if (!initFeatureMap()) { + return false; + } + Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); + if (entries != null) { + return entries.contains(value); + } + return false; + } + + /* + * Create the feature map if not already created. + */ + private boolean initFeatureMap() throws IOException { + if (__featuresMap == null) { + // Don't create map here, because next line may throw exception + final int replyCode = feat(); + if (replyCode == FTPReply.NOT_LOGGED_IN) { // 503 + return false; // NET-518; don't create empy map + } + boolean success = FTPReply.isPositiveCompletion(replyCode); + // we init the map here, so we don't keep trying if we know the command will fail + __featuresMap = new HashMap>(); + if (!success) { + return false; + } + for (String l : getReplyStrings()) { + if (l.startsWith(" ")) { // it's a FEAT entry + String key; + String value = ""; + int varsep = l.indexOf(' ', 1); + if (varsep > 0) { + key = l.substring(1, varsep); + value = l.substring(varsep + 1); + } else { + key = l.substring(1); + } + key = key.toUpperCase(Locale.ENGLISH); + Set entries = __featuresMap.get(key); + if (entries == null) { + entries = new HashSet(); + __featuresMap.put(key, entries); + } + entries.add(value); + } + } + } + return true; + } + + /** + * Reserve space on the server for the next file transfer. + * + * @param bytes The number of bytes which the server should allocate. + * @param recordSize The size of a file record. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean allocate(int bytes, int recordSize) throws IOException { + return FTPReply.isPositiveCompletion(allo(bytes, recordSize)); + } + + /** + * Issue a command and wait for the reply. + *

+ * Should only be used with commands that return replies on the + * command channel - do not use for LIST, NLST, MLSD etc. + * + * @param command The command to invoke + * @param params The parameters string, may be {@code null} + * @return True if successfully completed, false if not, in which case + * call {@link #getReplyCode()} or {@link #getReplyString()} + * to get the reason. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.0 + */ + public boolean doCommand(String command, String params) throws IOException { + return FTPReply.isPositiveCompletion(sendCommand(command, params)); + } + + /** + * Issue a command and wait for the reply, returning it as an array of strings. + *

+ * Should only be used with commands that return replies on the + * command channel - do not use for LIST, NLST, MLSD etc. + * + * @param command The command to invoke + * @param params The parameters string, may be {@code null} + * @return The array of replies, or {@code null} if the command failed, in which case + * call {@link #getReplyCode()} or {@link #getReplyString()} + * to get the reason. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.0 + */ + public String[] doCommandAsStrings(String command, String params) throws IOException { + boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params)); + if (success) { + return getReplyStrings(); + } else { + return null; + } + } + + /** + * Get file details using the MLST command + * + * @param pathname the file or directory to list, may be {@code null} + * @return the file details, may be {@code null} + * @throws IOException on error + * @since 3.0 + */ + public FTPFile mlistFile(String pathname) throws IOException { + boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname)); + if (success) { + String reply = getReplyStrings()[1]; + /* check the response makes sense. + * Must have space before fact(s) and between fact(s) and filename + * Fact(s) can be absent, so at least 3 chars are needed. + */ + if (reply.length() < 3 || reply.charAt(0) != ' ') { + throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'"); + } + String entry = reply.substring(1); // skip leading space for parser + return MLSxEntryParser.parseEntry(entry); + } else { + return null; + } + } + + /** + * Generate a directory listing for the current directory using the MLSD command. + * + * @return the array of file entries + * @throws IOException on error + * @since 3.0 + */ + public FTPFile[] mlistDir() throws IOException { + return mlistDir(null); + } + + /** + * Generate a directory listing using the MLSD command. + * + * @param pathname the directory name, may be {@code null} + * @return the array of file entries + * @throws IOException on error + * @since 3.0 + */ + public FTPFile[] mlistDir(String pathname) throws IOException { + FTPListParseEngine engine = initiateMListParsing(pathname); + return engine.getFiles(); + } + + /** + * Generate a directory listing using the MLSD command. + * + * @param pathname the directory name, may be {@code null} + * @param filter the filter to apply to the responses + * @return the array of file entries + * @throws IOException on error + * @since 3.0 + */ + public FTPFile[] mlistDir(String pathname, FTPFileFilter filter) throws IOException { + FTPListParseEngine engine = initiateMListParsing(pathname); + return engine.getFiles(filter); + } + + /** + * Restart a STREAM_TRANSFER_MODE file transfer starting + * from the given offset. This will only work on FTP servers supporting + * the REST comand for the stream transfer mode. However, most FTP + * servers support this. Any subsequent file transfer will start + * reading or writing the remote file from the indicated offset. + * + * @param offset The offset into the remote file at which to start the + * next file transfer. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @since 3.1 (changed from private to protected) + */ + protected boolean restart(long offset) throws IOException { + __restartOffset = 0; + return FTPReply.isPositiveIntermediate(rest(Long.toString(offset))); + } + + /** + * Sets the restart offset for file transfers. + *

+ * The restart command is not sent to the server immediately. + * It is sent when a data connection is created as part of a + * subsequent command. + * The restart marker is reset to zero after use. + *

+ *

+ * Note: This method should only be invoked immediately prior to + * the transfer to which it applies. + * + * @param offset The offset into the remote file at which to start the + * next file transfer. This must be a value greater than or + * equal to zero. + */ + public void setRestartOffset(long offset) { + if (offset >= 0) { + __restartOffset = offset; + } + } + + /** + * Fetches the restart offset. + * + * @return offset The offset into the remote file at which to start the + * next file transfer. + */ + public long getRestartOffset() { + return __restartOffset; + } + + /** + * Renames a remote file. + * + * @param from The name of the remote file to rename. + * @param to The new name of the remote file. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean rename(String from, String to) throws IOException { + if (!FTPReply.isPositiveIntermediate(rnfr(from))) { + return false; + } + + return FTPReply.isPositiveCompletion(rnto(to)); + } + + /** + * Abort a transfer in progress. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean abort() throws IOException { + return FTPReply.isPositiveCompletion(abor()); + } + + /** + * Deletes a file on the FTP server. + * + * @param pathname The pathname of the file to be deleted. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean deleteFile(String pathname) throws IOException { + return FTPReply.isPositiveCompletion(dele(pathname)); + } + + /** + * Removes a directory on the FTP server (if empty). + * + * @param pathname The pathname of the directory to remove. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean removeDirectory(String pathname) throws IOException { + return FTPReply.isPositiveCompletion(rmd(pathname)); + } + + /** + * Creates a new subdirectory on the FTP server in the current directory + * (if a relative pathname is given) or where specified (if an absolute + * pathname is given). + * + * @param pathname The pathname of the directory to create. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean makeDirectory(String pathname) throws IOException { + return FTPReply.isPositiveCompletion(mkd(pathname)); + } + + /** + * Returns the pathname of the current working directory. + * + * @return The pathname of the current working directory. If it cannot + * be obtained, returns null. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String printWorkingDirectory() throws IOException { + if (pwd() != FTPReply.PATHNAME_CREATED) { + return null; + } + + return __parsePathname(_replyLines.get(_replyLines.size() - 1)); + } + + /** + * Send a site specific command. + * + * @param arguments The site specific command and arguments. + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean sendSiteCommand(String arguments) throws IOException { + return FTPReply.isPositiveCompletion(site(arguments)); + } + + /** + * Fetches the system type from the server and returns the string. + * This value is cached for the duration of the connection after the + * first call to this method. In other words, only the first time + * that you invoke this method will it issue a SYST command to the + * FTP server. FTPClient will remember the value and return the + * cached value until a call to disconnect. + *

+ * If the SYST command fails, and the system property + * {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead. + * + * @return The system type obtained from the server. Never null. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server (and the default + * system type property is not defined) + * @since 2.2 + */ + public String getSystemType() throws IOException { + //if (syst() == FTPReply.NAME_SYSTEM_TYPE) + // Technically, we should expect a NAME_SYSTEM_TYPE response, but + // in practice FTP servers deviate, so we soften the condition to + // a positive completion. + if (__systemName == null) { + if (FTPReply.isPositiveCompletion(syst())) { + // Assume that response is not empty here (cannot be null) + __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); + } else { + // Check if the user has provided a default for when the SYST command fails + String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT); + if (systDefault != null) { + __systemName = systDefault; + } else { + throw new IOException("Unable to determine system type - response: " + getReplyString()); + } + } + } + return __systemName; + } + + /** + * Fetches the system help information from the server and returns the + * full string. + * + * @return The system help string obtained from the server. null if the + * information could not be obtained. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String listHelp() throws IOException { + if (FTPReply.isPositiveCompletion(help())) { + return getReplyString(); + } + return null; + } + + /** + * Fetches the help information for a given command from the server and + * returns the full string. + * + * @param command The command on which to ask for help. + * @return The command help string obtained from the server. null if the + * information could not be obtained. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String listHelp(String command) throws IOException { + if (FTPReply.isPositiveCompletion(help(command))) { + return getReplyString(); + } + return null; + } + + /** + * Sends a NOOP command to the FTP server. This is useful for preventing + * server timeouts. + * + * @return True if successfully completed, false if not. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public boolean sendNoOp() throws IOException { + return FTPReply.isPositiveCompletion(noop()); + } + + /** + * Obtain a list of filenames in a directory (or just the name of a given + * file, which is not particularly useful). This information is obtained + * through the NLST command. If the given pathname is a directory and + * contains no files, a zero length array is returned only + * if the FTP server returned a positive completion code, otherwise + * null is returned (the FTP server returned a 550 error No files found.). + * If the directory is not empty, an array of filenames in the directory is + * returned. If the pathname corresponds + * to a file, only that file will be listed. The server may or may not + * expand glob expressions. + * + * @param pathname The file or directory to list. + * Warning: the server may treat a leading '-' as an + * option introducer. If so, try using an absolute path, + * or prefix the path with ./ (unix style servers). + * Some servers may support "--" as meaning end of options, + * in which case "-- -xyz" should work. + * @return The list of filenames contained in the given path. null if + * the list could not be obtained. If there are no filenames in + * the directory, a zero-length array is returned. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String[] listNames(String pathname) throws IOException { + Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname)); + + if (socket == null) { + return null; + } + + BufferedReader reader = + new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); + + ArrayList results = new ArrayList(); + String line; + while ((line = reader.readLine()) != null) { + results.add(line); + } + + reader.close(); + socket.close(); + + if (completePendingCommand()) { + String[] names = new String[results.size()]; + return results.toArray(names); + } + + return null; + } + + /** + * Obtain a list of filenames in the current working directory + * This information is obtained through the NLST command. If the current + * directory contains no files, a zero length array is returned only + * if the FTP server returned a positive completion code, otherwise, + * null is returned (the FTP server returned a 550 error No files found.). + * If the directory is not empty, an array of filenames in the directory is + * returned. + * + * @return The list of filenames contained in the current working + * directory. null if the list could not be obtained. + * If there are no filenames in the directory, a zero-length array + * is returned. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String[] listNames() throws IOException { + return listNames(null); + } + + /** + * Using the default system autodetect mechanism, obtain a + * list of file information for the current working directory + * or for just a single file. + *

+ * This information is obtained through the LIST command. The contents of + * the returned array is determined by the FTPFileEntryParser + * used. + *

+ * N.B. the LIST command does not generally return very precise timestamps. + * For recent files, the response usually contains hours and minutes (not seconds). + * For older files, the output may only contain a date. + * If the server supports it, the MLSD command returns timestamps with a precision + * of seconds, and may include milliseconds. See {@link #mlistDir()} + * + * @param pathname The file or directory to list. Since the server may + * or may not expand glob expressions, using them here + * is not recommended and may well cause this method to + * fail. + * Also, some servers treat a leading '-' as being an option. + * To avoid this interpretation, use an absolute pathname + * or prefix the pathname with ./ (unix style servers). + * Some servers may support "--" as meaning end of options, + * in which case "-- -xyz" should work. + * @return The list of file information contained in the given path in + * the format determined by the autodetection mechanism + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection + * as a result of the client being idle or some other + * reason causing the server to send FTP reply code 421. + * This exception may be caught either as an IOException + * or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply + * from the server. + * @throws ParserInitializationException Thrown if the + * parserKey + * parameter cannot be + * resolved by the selected parser factory. + * In the DefaultFTPEntryParserFactory, this will + * happen when parserKey is neither + * the fully qualified class name of a class + * implementing the interface + * FTPFileEntryParser + * nor a string containing one of the recognized keys + * mapping to such a parser or if class loader + * security issues prevent its being loaded. + * @see DefaultFTPFileEntryParserFactory + * @see FTPFileEntryParserFactory + * @see FTPFileEntryParser + */ + public FTPFile[] listFiles(String pathname) throws IOException { + FTPListParseEngine engine = initiateListParsing((String) null, pathname); + return engine.getFiles(); + } + + /** + * Using the default system autodetect mechanism, obtain a + * list of file information for the current working directory. + *

+ * This information is obtained through the LIST command. The contents of + * the returned array is determined by the FTPFileEntryParser + * used. + *

+ * N.B. the LIST command does not generally return very precise timestamps. + * For recent files, the response usually contains hours and minutes (not seconds). + * For older files, the output may only contain a date. + * If the server supports it, the MLSD command returns timestamps with a precision + * of seconds, and may include milliseconds. See {@link #mlistDir()} + * + * @return The list of file information contained in the current directory + * in the format determined by the autodetection mechanism. + *

+ * NOTE: This array may contain null members if any of the + * individual file listings failed to parse. The caller should + * check each entry for null before referencing it. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection + * as a result of the client being idle or some other + * reason causing the server to send FTP reply code 421. + * This exception may be caught either as an IOException + * or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply + * from the server. + * @throws ParserInitializationException Thrown if the + * parserKey + * parameter cannot be + * resolved by the selected parser factory. + * In the DefaultFTPEntryParserFactory, this will + * happen when parserKey is neither + * the fully qualified class name of a class + * implementing the interface + * FTPFileEntryParser + * nor a string containing one of the recognized keys + * mapping to such a parser or if class loader + * security issues prevent its being loaded. + * @see DefaultFTPFileEntryParserFactory + * @see FTPFileEntryParserFactory + * @see FTPFileEntryParser + */ + public FTPFile[] listFiles() throws IOException { + return listFiles((String) null); + } + + /** + * Version of {@link #listFiles(String)} which allows a filter to be provided. + * For example: listFiles("site", FTPFileFilters.DIRECTORY); + * + * @param pathname the initial path, may be null + * @param filter the filter, non-null + * @return the list of FTPFile entries. + * @throws IOException on error + * @since 2.2 + */ + public FTPFile[] listFiles(String pathname, FTPFileFilter filter) throws IOException { + FTPListParseEngine engine = initiateListParsing((String) null, pathname); + return engine.getFiles(filter); + } + + /** + * Using the default system autodetect mechanism, obtain a + * list of directories contained in the current working directory. + *

+ * This information is obtained through the LIST command. The contents of + * the returned array is determined by the FTPFileEntryParser + * used. + *

+ * N.B. the LIST command does not generally return very precise timestamps. + * For recent files, the response usually contains hours and minutes (not seconds). + * For older files, the output may only contain a date. + * If the server supports it, the MLSD command returns timestamps with a precision + * of seconds, and may include milliseconds. See {@link #mlistDir()} + * + * @return The list of directories contained in the current directory + * in the format determined by the autodetection mechanism. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection + * as a result of the client being idle or some other + * reason causing the server to send FTP reply code 421. + * This exception may be caught either as an IOException + * or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply + * from the server. + * @throws ParserInitializationException Thrown if the + * parserKey + * parameter cannot be + * resolved by the selected parser factory. + * In the DefaultFTPEntryParserFactory, this will + * happen when parserKey is neither + * the fully qualified class name of a class + * implementing the interface + * FTPFileEntryParser + * nor a string containing one of the recognized keys + * mapping to such a parser or if class loader + * security issues prevent its being loaded. + * @see DefaultFTPFileEntryParserFactory + * @see FTPFileEntryParserFactory + * @see FTPFileEntryParser + * @since 3.0 + */ + public FTPFile[] listDirectories() throws IOException { + return listDirectories((String) null); + } + + /** + * Using the default system autodetect mechanism, obtain a + * list of directories contained in the specified directory. + *

+ * This information is obtained through the LIST command. The contents of + * the returned array is determined by the FTPFileEntryParser + * used. + *

+ * N.B. the LIST command does not generally return very precise timestamps. + * For recent files, the response usually contains hours and minutes (not seconds). + * For older files, the output may only contain a date. + * If the server supports it, the MLSD command returns timestamps with a precision + * of seconds, and may include milliseconds. See {@link #mlistDir()} + * + * @param parent the starting directory + * @return The list of directories contained in the specified directory + * in the format determined by the autodetection mechanism. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection + * as a result of the client being idle or some other + * reason causing the server to send FTP reply code 421. + * This exception may be caught either as an IOException + * or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply + * from the server. + * @throws ParserInitializationException Thrown if the + * parserKey + * parameter cannot be + * resolved by the selected parser factory. + * In the DefaultFTPEntryParserFactory, this will + * happen when parserKey is neither + * the fully qualified class name of a class + * implementing the interface + * FTPFileEntryParser + * nor a string containing one of the recognized keys + * mapping to such a parser or if class loader + * security issues prevent its being loaded. + * @see DefaultFTPFileEntryParserFactory + * @see FTPFileEntryParserFactory + * @see FTPFileEntryParser + * @since 3.0 + */ + public FTPFile[] listDirectories(String parent) throws IOException { + return listFiles(parent, FTPFileFilters.DIRECTORIES); + } + + /** + * Using the default autodetect mechanism, initialize an FTPListParseEngine + * object containing a raw file information for the current working + * directory on the server + * This information is obtained through the LIST command. This object + * is then capable of being iterated to return a sequence of FTPFile + * objects with information filled in by the + * FTPFileEntryParser used. + *

+ * This method differs from using the listFiles() methods in that + * expensive FTPFile objects are not created until needed which may be + * an advantage on large lists. + * + * @return A FTPListParseEngine object that holds the raw information and + * is capable of providing parsed FTPFile objects, one for each file + * containing information contained in the given path in the format + * determined by the parser parameter. Null will be + * returned if a data connection cannot be opened. If the current working + * directory contains no files, an empty array will be the return. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @throws ParserInitializationException Thrown if the + * autodetect mechanism cannot + * resolve the type of system we are connected with. + * @see FTPListParseEngine + */ + public FTPListParseEngine initiateListParsing() throws IOException { + return initiateListParsing((String) null); + } + + /** + * Using the default autodetect mechanism, initialize an FTPListParseEngine + * object containing a raw file information for the supplied directory. + * This information is obtained through the LIST command. This object + * is then capable of being iterated to return a sequence of FTPFile + * objects with information filled in by the + * FTPFileEntryParser used. + *

+ * The server may or may not expand glob expressions. You should avoid + * using glob expressions because the return format for glob listings + * differs from server to server and will likely cause this method to fail. + *

+ * This method differs from using the listFiles() methods in that + * expensive FTPFile objects are not created until needed which may be + * an advantage on large lists. + * + *

+   *    FTPClient f=FTPClient();
+   *    f.connect(server);
+   *    f.login(username, password);
+   *    FTPListParseEngine engine = f.initiateListParsing(directory);
+   *
+   *    while (engine.hasNext()) {
+   *       FTPFile[] files = engine.getNext(25);  // "page size" you want
+   *       //do whatever you want with these files, display them, etc.
+   *       //expensive FTPFile objects not created until needed.
+   *    }
+   * 
+ * + * @param pathname the starting directory + * @return A FTPListParseEngine object that holds the raw information and + * is capable of providing parsed FTPFile objects, one for each file + * containing information contained in the given path in the format + * determined by the parser parameter. Null will be + * returned if a data connection cannot be opened. If the current working + * directory contains no files, an empty array will be the return. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @throws ParserInitializationException Thrown if the + * autodetect mechanism cannot + * resolve the type of system we are connected with. + * @see FTPListParseEngine + */ + public FTPListParseEngine initiateListParsing(String pathname) throws IOException { + return initiateListParsing((String) null, pathname); + } + + /** + * Using the supplied parser key, initialize an FTPListParseEngine + * object containing a raw file information for the supplied directory. + * This information is obtained through the LIST command. This object + * is then capable of being iterated to return a sequence of FTPFile + * objects with information filled in by the + * FTPFileEntryParser used. + *

+ * The server may or may not expand glob expressions. You should avoid + * using glob expressions because the return format for glob listings + * differs from server to server and will likely cause this method to fail. + *

+ * This method differs from using the listFiles() methods in that + * expensive FTPFile objects are not created until needed which may be + * an advantage on large lists. + * + * @param parserKey A string representing a designated code or fully-qualified + * class name of an FTPFileEntryParser that should be + * used to parse each server file listing. + * May be {@code null}, in which case the code checks first + * the system property {@link #FTP_SYSTEM_TYPE}, and if that is + * not defined the SYST command is used to provide the value. + * To allow for arbitrary system types, the return from the + * SYST command is used to look up an alias for the type in the + * {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available. + * @param pathname the starting directory + * @return A FTPListParseEngine object that holds the raw information and + * is capable of providing parsed FTPFile objects, one for each file + * containing information contained in the given path in the format + * determined by the parser parameter. Null will be + * returned if a data connection cannot be opened. If the current working + * directory contains no files, an empty array will be the return. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @throws ParserInitializationException Thrown if the + * parserKey + * parameter cannot be + * resolved by the selected parser factory. + * In the DefaultFTPEntryParserFactory, this will + * happen when parserKey is neither + * the fully qualified class name of a class + * implementing the interface + * FTPFileEntryParser + * nor a string containing one of the recognized keys + * mapping to such a parser or if class loader + * security issues prevent its being loaded. + * @see FTPListParseEngine + */ + public FTPListParseEngine initiateListParsing(String parserKey, String pathname) + throws IOException { + __createParser(parserKey); // create and cache parser + return initiateListParsing(__entryParser, pathname); + } + + // package access for test purposes + void __createParser(String parserKey) throws IOException { + // We cache the value to avoid creation of a new object every + // time a file listing is generated. + // Note: we don't check against a null parserKey (NET-544) + if (__entryParser == null || (parserKey != null && !__entryParserKey.equals(parserKey))) { + if (null != parserKey) { + // if a parser key was supplied in the parameters, + // use that to create the parser + __entryParser = __parserFactory.createFileEntryParser(parserKey); + __entryParserKey = parserKey; + } else { + // if no parserKey was supplied, check for a configuration + // in the params, and if it has a non-empty system type, use that. + if (null != __configuration && __configuration.getServerSystemKey().length() > 0) { + __entryParser = __parserFactory.createFileEntryParser(__configuration); + __entryParserKey = __configuration.getServerSystemKey(); + } else { + // if a parserKey hasn't been supplied, and a configuration + // hasn't been supplied, and the override property is not set + // then autodetect by calling + // the SYST command and use that to choose the parser. + String systemType = System.getProperty(FTP_SYSTEM_TYPE); + if (systemType == null) { + systemType = getSystemType(); // cannot be null + Properties override = getOverrideProperties(); + if (override != null) { + String newType = override.getProperty(systemType); + if (newType != null) { + systemType = newType; + } + } + } + if (null != __configuration) { // system type must have been empty above + __entryParser = __parserFactory.createFileEntryParser( + new FTPClientConfig(systemType, __configuration)); + } else { + __entryParser = __parserFactory.createFileEntryParser(systemType); + } + __entryParserKey = systemType; + } + } + } + } + + /** + * private method through which all listFiles() and + * initiateListParsing methods pass once a parser is determined. + * + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + * @see FTPListParseEngine + */ + private FTPListParseEngine initiateListParsing(FTPFileEntryParser parser, String pathname) + throws IOException { + Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname)); + + FTPListParseEngine engine = new FTPListParseEngine(parser, __configuration); + if (socket == null) { + return engine; + } + + try { + engine.readServerList(socket.getInputStream(), getControlEncoding()); + } finally { + Util.closeQuietly(socket); + } + + completePendingCommand(); + return engine; + } + + /** + * Initiate list parsing for MLSD listings. + * + * @return the engine + * @throws IOException + */ + private FTPListParseEngine initiateMListParsing(String pathname) throws IOException { + Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname); + FTPListParseEngine engine = + new FTPListParseEngine(MLSxEntryParser.getInstance(), __configuration); + if (socket == null) { + return engine; + } + + try { + engine.readServerList(socket.getInputStream(), getControlEncoding()); + } finally { + Util.closeQuietly(socket); + completePendingCommand(); + } + return engine; + } + + /** + * @param pathname the initial pathname + * @return the adjusted string with "-a" added if necessary + * @since 2.0 + */ + protected String getListArguments(String pathname) { + if (getListHiddenFiles()) { + if (pathname != null) { + StringBuilder sb = new StringBuilder(pathname.length() + 3); + sb.append("-a "); + sb.append(pathname); + return sb.toString(); + } else { + return "-a"; + } + } + + return pathname; + } + + /** + * Issue the FTP STAT command to the server. + * + * @return The status information returned by the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String getStatus() throws IOException { + if (FTPReply.isPositiveCompletion(stat())) { + return getReplyString(); + } + return null; + } + + /** + * Issue the FTP STAT command to the server for a given pathname. This + * should produce a listing of the file or directory. + * + * @param pathname the filename + * @return The status information returned by the server. + * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a + * result + * of the client being idle or some other reason causing the server + * to send FTP reply code 421. This exception may be caught either + * as an IOException or independently as itself. + * @throws IOException If an I/O error occurs while either sending a + * command to the server or receiving a reply from the server. + */ + public String getStatus(String pathname) throws IOException { + if (FTPReply.isPositiveCompletion(stat(pathname))) { + return getReplyString(); + } + return null; + } + + /** + * Issue the FTP MDTM command (not supported by all servers) to retrieve the last + * modification time of a file. The modification string should be in the + * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in + * GMT, but not all FTP servers honour this. + * + * @param pathname The file path to query. + * @return A string representing the last file modification time in YYYYMMDDhhmmss + * format. + * @throws IOException if an I/O error occurs. + * @since 2.0 + */ + public String getModificationTime(String pathname) throws IOException { + if (FTPReply.isPositiveCompletion(mdtm(pathname))) { + return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space + } + return null; + } + + /** + * Issue the FTP MDTM command (not supported by all servers) to retrieve the last + * modification time of a file. The modification string should be in the + * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in + * GMT, but not all FTP servers honour this. + * + * @param pathname The file path to query. + * @return A FTPFile representing the last file modification time, may be {@code null}. + * The FTPFile timestamp will be null if a parse error occurs. + * @throws IOException if an I/O error occurs. + * @since 3.4 + */ + public FTPFile mdtmFile(String pathname) throws IOException { + if (FTPReply.isPositiveCompletion(mdtm(pathname))) { + String reply = + getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space + FTPFile file = new FTPFile(); + file.setName(pathname); + file.setRawListing(reply); + file.setTimestamp(MLSxEntryParser.parseGMTdateTime(reply)); + return file; + } + return null; + } + + /** + * Issue the FTP MFMT command (not supported by all servers) which sets the last + * modified time of a file. + * + * The timestamp should be in the form YYYYMMDDhhmmss. It should also + * be in GMT, but not all servers honour this. + * + * An FTP server would indicate its support of this feature by including "MFMT" + * in its response to the FEAT command, which may be retrieved by FTPClient.features() + * + * @param pathname The file path for which last modified time is to be changed. + * @param timeval The timestamp to set to, in YYYYMMDDhhmmss format. + * @return true if successfully set, false if not + * @throws IOException if an I/O error occurs. + * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 + * @since 2.2 + */ + public boolean setModificationTime(String pathname, String timeval) throws IOException { + return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval))); + } + + /** + * Set the internal buffer size for buffered data streams. + * + * @param bufSize The size of the buffer. Use a non-positive value to use the default. + */ + public void setBufferSize(int bufSize) { + __bufferSize = bufSize; + } + + /** + * Retrieve the current internal buffer size for buffered data streams. + * + * @return The current buffer size. + */ + public int getBufferSize() { + return __bufferSize; + } + + /** + * Sets the value to be used for the data socket SO_SNDBUF option. + * If the value is positive, the option will be set when the data socket has been created. + * + * @param bufSize The size of the buffer, zero or negative means the value is ignored. + * @since 3.3 + */ + public void setSendDataSocketBufferSize(int bufSize) { + __sendDataSocketBufferSize = bufSize; + } + + /** + * Retrieve the value to be used for the data socket SO_SNDBUF option. + * + * @return The current buffer size. + * @since 3.3 + */ + public int getSendDataSocketBufferSize() { + return __sendDataSocketBufferSize; + } + + /** + * Sets the value to be used for the data socket SO_RCVBUF option. + * If the value is positive, the option will be set when the data socket has been created. + * + * @param bufSize The size of the buffer, zero or negative means the value is ignored. + * @since 3.3 + */ + public void setReceieveDataSocketBufferSize(int bufSize) { + __receiveDataSocketBufferSize = bufSize; + } + + /** + * Retrieve the value to be used for the data socket SO_RCVBUF option. + * + * @return The current buffer size. + * @since 3.3 + */ + public int getReceiveDataSocketBufferSize() { + return __receiveDataSocketBufferSize; + } + + /** + * Implementation of the {@link Configurable Configurable} interface. + * In the case of this class, configuring merely makes the config object available for the + * factory methods that construct parsers. + * + * @param config {@link FTPClientConfig FTPClientConfig} object used to + * provide non-standard configurations to the parser. + * @since 1.4 + */ + @Override public void configure(FTPClientConfig config) { + this.__configuration = config; + } + + /** + * You can set this to true if you would like to get hidden files when {@link #listFiles} too. + * A LIST -a will be issued to the ftp server. + * It depends on your ftp server if you need to call this method, also dont expect to get rid + * of hidden files if you call this method with "false". + * + * @param listHiddenFiles true if hidden files should be listed + * @since 2.0 + */ + public void setListHiddenFiles(boolean listHiddenFiles) { + this.__listHiddenFiles = listHiddenFiles; + } + + /** + * @return the current state + * @see #setListHiddenFiles(boolean) + * @since 2.0 + */ + public boolean getListHiddenFiles() { + return this.__listHiddenFiles; + } + + /** + * Whether should attempt to use EPSV with IPv4. + * Default (if not set) is false + * + * @return true if should attempt EPSV + * @since 2.2 + */ + public boolean isUseEPSVwithIPv4() { + return __useEPSVwithIPv4; + } + + /** + * Set whether to use EPSV with IPv4. + * Might be worth enabling in some circumstances. + * + * For example, when using IPv4 with NAT it + * may work with some rare configurations. + * E.g. if FTP server has a static PASV address (external network) + * and the client is coming from another internal network. + * In that case the data connection after PASV command would fail, + * while EPSV would make the client succeed by taking just the port. + * + * @param selected value to set. + * @since 2.2 + */ + public void setUseEPSVwithIPv4(boolean selected) { + this.__useEPSVwithIPv4 = selected; + } + + /** + * Set the listener to be used when performing store/retrieve operations. + * The default value (if not set) is {@code null}. + * + * @param listener to be used, may be {@code null} to disable + * @since 3.0 + */ + public void setCopyStreamListener(CopyStreamListener listener) { + __copyStreamListener = listener; + } + + /** + * Obtain the currently active listener. + * + * @return the listener, may be {@code null} + * @since 3.0 + */ + public CopyStreamListener getCopyStreamListener() { + return __copyStreamListener; + } + + /** + * Set the time to wait between sending control connection keepalive messages + * when processing file upload or download. + * + * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables. + * @see #setControlKeepAliveReplyTimeout(int) + * @since 3.0 + */ + public void setControlKeepAliveTimeout(long controlIdle) { + __controlKeepAliveTimeout = controlIdle * 1000; + } + + /** + * Get the time to wait between sending control connection keepalive messages. + * + * @return the number of seconds between keepalive messages. + * @since 3.0 + */ + public long getControlKeepAliveTimeout() { + return __controlKeepAliveTimeout / 1000; + } + + /** + * Set how long to wait for control keep-alive message replies. + * + * @param timeout number of milliseconds to wait (defaults to 1000) + * @see #setControlKeepAliveTimeout(long) + * @since 3.0 + */ + public void setControlKeepAliveReplyTimeout(int timeout) { + __controlKeepAliveReplyTimeout = timeout; + } + + /** + * Get how long to wait for control keep-alive message replies. + * + * @return wait time in msec + * @since 3.0 + */ + public int getControlKeepAliveReplyTimeout() { + return __controlKeepAliveReplyTimeout; + } + + /** + * Enable or disable passive mode NAT workaround. + * If enabled, a site-local PASV mode reply address will be replaced with the + * remote host address to which the PASV mode request was sent + * (unless that is also a site local address). + * This gets around the problem that some NAT boxes may change the + * reply. + * + * The default is true, i.e. site-local replies are replaced. + * + * @param enabled true to enable replacing internal IP's in passive + * mode. + * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead + */ + @Deprecated public void setPassiveNatWorkaround(boolean enabled) { + if (enabled) { + this.__passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); + } else { + this.__passiveNatWorkaroundStrategy = null; + } + } + + /** + * Set the workaround strategy to replace the PASV mode reply addresses. + * This gets around the problem that some NAT boxes may change the reply. + * + * The default implementation is {@code NatServerResolverImpl}, i.e. site-local + * replies are replaced. + * + * @param resolver strategy to replace internal IP's in passive mode + * or null to disable the workaround (i.e. use PASV mode reply address.) + * @since 3.6 + */ + public void setPassiveNatWorkaroundStrategy(HostnameResolver resolver) { + this.__passiveNatWorkaroundStrategy = resolver; + } + + /** + * Strategy interface for updating host names received from FTP server + * for passive NAT workaround. + * + * @since 3.6 + */ + public static interface HostnameResolver { + String resolve(String hostname) throws UnknownHostException; + } + + /** + * Default strategy for passive NAT workaround (site-local + * replies are replaced.) + * + * @since 3.6 + */ + public static class NatServerResolverImpl implements HostnameResolver { + private FTPClient client; + + public NatServerResolverImpl(FTPClient client) { + this.client = client; + } + + @Override public String resolve(String hostname) throws UnknownHostException { + String newHostname = hostname; + InetAddress host = InetAddress.getByName(newHostname); + // reply is a local address, but target is not - assume NAT box changed the PASV reply + if (host.isSiteLocalAddress()) { + InetAddress remote = this.client.getRemoteAddress(); + if (!remote.isSiteLocalAddress()) { + newHostname = remote.getHostAddress(); + } + } + return newHostname; + } + } + + private OutputStream getBufferedOutputStream(OutputStream outputStream) { + if (__bufferSize > 0) { + return new BufferedOutputStream(outputStream, __bufferSize); + } + return new BufferedOutputStream(outputStream); + } + + private InputStream getBufferedInputStream(InputStream inputStream) { + if (__bufferSize > 0) { + return new BufferedInputStream(inputStream, __bufferSize); + } + return new BufferedInputStream(inputStream); + } + + // @since 3.0 + private static class CSL implements CopyStreamListener { + + private final FTPClient parent; + private final long idle; + private final int currentSoTimeout; + + private long time = System.currentTimeMillis(); + private int notAcked; + private OnFtpInputStreamListener listener; + private long lTime = time; + + CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException { + this(parent, idleTime, maxWait, null); + } + + CSL(FTPClient parent, long idleTime, int maxWait, OnFtpInputStreamListener listener) + throws SocketException { + this.idle = idleTime; + this.parent = parent; + this.currentSoTimeout = parent.getSoTimeout(); + parent.setSoTimeout(maxWait); + this.listener = listener; + } + + @Override public void bytesTransferred(CopyStreamEvent event) { + bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), + event.getStreamSize()); + } + + @Override public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, + long streamSize) { + long now = System.currentTimeMillis(); + if (now - time > idle) { + try { + parent.__noop(); + } catch (SocketTimeoutException e) { + notAcked++; + } catch (IOException e) { + // Ignored + } + + time = now; + } + //if (now - lTime > 100) { + if (listener != null) { + listener.onFtpInputStream(parent, totalBytesTransferred, bytesTransferred, streamSize); + } + //lTime = now; + //} + } + + void cleanUp() throws IOException { + try { + while (notAcked-- > 0) { + parent.__getReplyNoReport(); + } + } finally { + parent.setSoTimeout(currentSoTimeout); + } + } + } + + /** + * Merge two copystream listeners, either or both of which may be null. + * + * @param local the listener used by this class, may be null + * @return a merged listener or a single listener or null + * @since 3.0 + */ + private CopyStreamListener __mergeListeners(CopyStreamListener local) { + if (local == null) { + return __copyStreamListener; + } + if (__copyStreamListener == null) { + return local; + } + // Both are non-null + CopyStreamAdapter merged = new CopyStreamAdapter(); + merged.addCopyStreamListener(local); + merged.addCopyStreamListener(__copyStreamListener); + return merged; + } + + /** + * Enables or disables automatic server encoding detection (only UTF-8 supported). + *

+ * Does not affect existing connections; must be invoked before a connection is established. + * + * @param autodetect If true, automatic server encoding detection will be enabled. + */ + public void setAutodetectUTF8(boolean autodetect) { + __autodetectEncoding = autodetect; + } + + /** + * Tells if automatic server encoding detection is enabled or disabled. + * + * @return true, if automatic server encoding detection is enabled. + */ + public boolean getAutodetectUTF8() { + return __autodetectEncoding; + } + + // Method for use by unit test code only + FTPFileEntryParser getEntryParser() { + return __entryParser; + } + + // DEPRECATED METHODS - for API compatibility only - DO NOT USE + + /** + * @return the name + * @throws IOException on error + * @deprecated use {@link #getSystemType()} instead + */ + @Deprecated public String getSystemName() throws IOException { + if (__systemName == null && FTPReply.isPositiveCompletion(syst())) { + __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); + } + return __systemName; + } +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ +/* kate: indent-width 4; replace-tabs on; */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java new file mode 100644 index 00000000..abe8902e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java @@ -0,0 +1,713 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import aria.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl; +import aria.apache.commons.net.ftp.parser.FTPTimestampParserImpl; +import java.text.DateFormatSymbols; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeMap; + +/** + *

+ * This class implements an alternate means of configuring the + * {@link FTPClient FTPClient} object and + * also subordinate objects which it uses. Any class implementing the + * {@link Configurable Configurable } + * interface can be configured by this object. + *

+ * In particular this class was designed primarily to support configuration + * of FTP servers which express file timestamps in formats and languages + * other than those for the US locale, which although it is the most common + * is not universal. Unfortunately, nothing in the FTP spec allows this to + * be determined in an automated way, so manual configuration such as this + * is necessary. + *

+ * This functionality was designed to allow existing clients to work exactly + * as before without requiring use of this component. This component should + * only need to be explicitly invoked by the user of this package for problem + * cases that previous implementations could not solve. + *

+ *

Examples of use of FTPClientConfig

+ * Use cases: + * You are trying to access a server that + *
    + *
  • lists files with timestamps that use month names in languages other + * than English
  • + *
  • lists files with timestamps that use date formats other + * than the American English "standard" MM dd yyyy
  • + *
  • is in different timezone and you need accurate timestamps for + * dependency checking as in Ant
  • + *
+ *

+ * Unpaged (whole list) access on a UNIX server that uses French month names + * but uses the "standard" MMM d yyyy date formatting + *

+ *    FTPClient f=FTPClient();
+ *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ *    conf.setServerLanguageCode("fr");
+ *    f.configure(conf);
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = listFiles(directory);
+ * 
+ *

+ * Paged access on a UNIX server that uses Danish month names + * and "European" date formatting in Denmark's time zone, when you + * are in some other time zone. + *

+ *    FTPClient f=FTPClient();
+ *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ *    conf.setServerLanguageCode("da");
+ *    conf.setDefaultDateFormat("d MMM yyyy");
+ *    conf.setRecentDateFormat("d MMM HH:mm");
+ *    conf.setTimeZoneId("Europe/Copenhagen");
+ *    f.configure(conf);
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPListParseEngine engine =
+ *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
+ *
+ *    while (engine.hasNext()) {
+ *       FTPFile[] files = engine.getNext(25);  // "page size" you want
+ *       //do whatever you want with these files, display them, etc.
+ *       //expensive FTPFile objects not created until needed.
+ *    }
+ * 
+ *

+ * Unpaged (whole list) access on a VMS server that uses month names + * in a language not {@link #getSupportedLanguageCodes() supported} by the system. + * but uses the "standard" MMM d yyyy date formatting + *

+ *    FTPClient f=FTPClient();
+ *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
+ *    conf.setShortMonthNames(
+ *        "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
+ *    f.configure(conf);
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = listFiles(directory);
+ * 
+ *

+ * Unpaged (whole list) access on a Windows-NT server in a different time zone. + * (Note, since the NT Format uses numeric date formatting, language issues + * are irrelevant here). + *

+ *    FTPClient f=FTPClient();
+ *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
+ *    conf.setTimeZoneId("America/Denver");
+ *    f.configure(conf);
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = listFiles(directory);
+ * 
+ * Unpaged (whole list) access on a Windows-NT server in a different time zone + * but which has been configured to use a unix-style listing format. + *
+ *    FTPClient f=FTPClient();
+ *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ *    conf.setTimeZoneId("America/Denver");
+ *    f.configure(conf);
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = listFiles(directory);
+ * 
+ * + * @see Configurable + * @see FTPClient + * @see FTPTimestampParserImpl#configure(FTPClientConfig) + * @see ConfigurableFTPFileEntryParserImpl + * @since 1.4 + */ +public class FTPClientConfig { + + /** + * Identifier by which a unix-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_UNIX = "UNIX"; + + /** + * Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are + * trimmed from file names. This is to maintain backwards compatibility with + * the original behaviour of the parser which ignored multiple spaces between the date + * and the start of the file name. + * + * @since 3.4 + */ + public static final String SYST_UNIX_TRIM_LEADING = "UNIX_LTRIM"; + + /** + * Identifier by which a vms-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_VMS = "VMS"; + + /** + * Identifier by which a WindowsNT-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_NT = "WINDOWS"; + + /** + * Identifier by which an OS/2-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_OS2 = "OS/2"; + + /** + * Identifier by which an OS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_OS400 = "OS/400"; + + /** + * Identifier by which an AS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_AS400 = "AS/400"; + + /** + * Identifier by which an MVS-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_MVS = "MVS"; + + /** + * Some servers return an "UNKNOWN Type: L8" message + * in response to the SYST command. We set these to be a Unix-type system. + * This may happen if the ftpd in question was compiled without system + * information. + * + * NET-230 - Updated to be UPPERCASE so that the check done in + * createFileEntryParser will succeed. + * + * @since 1.5 + */ + public static final String SYST_L8 = "TYPE: L8"; + + /** + * Identifier by which an Netware-based ftp server is known throughout + * the commons-net ftp system. + * + * @since 1.5 + */ + public static final String SYST_NETWARE = "NETWARE"; + + /** + * Identifier by which a Mac pre OS-X -based ftp server is known throughout + * the commons-net ftp system. + * + * @since 3.1 + */ + // Full string is "MACOS Peter's Server"; the substring below should be enough + public static final String SYST_MACOS_PETER = "MACOS PETER"; // NET-436 + + private final String serverSystemKey; + private String defaultDateFormatStr = null; + private String recentDateFormatStr = null; + private boolean lenientFutureDates = true; // NET-407 + private String serverLanguageCode = null; + private String shortMonthNames = null; + private String serverTimeZoneId = null; + private boolean saveUnparseableEntries = false; + + /** + * The main constructor for an FTPClientConfig object + * + * @param systemKey key representing system type of the server being + * connected to. See {@link #getServerSystemKey() serverSystemKey} + * If set to the empty string, then FTPClient uses the system type returned by the server. + * However this is not recommended for general use; + * the correct system type should be set if it is known. + */ + public FTPClientConfig(String systemKey) { + this.serverSystemKey = systemKey; + } + + /** + * Convenience constructor mainly for use in testing. + * Constructs a UNIX configuration. + */ + public FTPClientConfig() { + this(SYST_UNIX); + } + + /** + * Constructor which allows setting of the format string member fields + * + * @param systemKey key representing system type of the server being + * connected to. See + * {@link #getServerSystemKey() serverSystemKey} + * @param defaultDateFormatStr See + * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * @param recentDateFormatStr See + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} + * @since 3.6 + */ + public FTPClientConfig(String systemKey, String defaultDateFormatStr, + String recentDateFormatStr) { + this(systemKey); + this.defaultDateFormatStr = defaultDateFormatStr; + this.recentDateFormatStr = recentDateFormatStr; + } + + /** + * Constructor which allows setting of most member fields + * + * @param systemKey key representing system type of the server being + * connected to. See + * {@link #getServerSystemKey() serverSystemKey} + * @param defaultDateFormatStr See + * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * @param recentDateFormatStr See + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} + * @param serverLanguageCode See + * {@link #setServerLanguageCode(String) serverLanguageCode} + * @param shortMonthNames See + * {@link #setShortMonthNames(String) shortMonthNames} + * @param serverTimeZoneId See + * {@link #setServerTimeZoneId(String) serverTimeZoneId} + */ + public FTPClientConfig(String systemKey, String defaultDateFormatStr, String recentDateFormatStr, + String serverLanguageCode, String shortMonthNames, String serverTimeZoneId) { + this(systemKey); + this.defaultDateFormatStr = defaultDateFormatStr; + this.recentDateFormatStr = recentDateFormatStr; + this.serverLanguageCode = serverLanguageCode; + this.shortMonthNames = shortMonthNames; + this.serverTimeZoneId = serverTimeZoneId; + } + + /** + * Constructor which allows setting of all member fields + * + * @param systemKey key representing system type of the server being + * connected to. See + * {@link #getServerSystemKey() serverSystemKey} + * @param defaultDateFormatStr See + * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * @param recentDateFormatStr See + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} + * @param serverLanguageCode See + * {@link #setServerLanguageCode(String) serverLanguageCode} + * @param shortMonthNames See + * {@link #setShortMonthNames(String) shortMonthNames} + * @param serverTimeZoneId See + * {@link #setServerTimeZoneId(String) serverTimeZoneId} + * @param lenientFutureDates See + * {@link #setLenientFutureDates(boolean) lenientFutureDates} + * @param saveUnparseableEntries See + * {@link #setUnparseableEntries(boolean) saveUnparseableEntries} + */ + public FTPClientConfig(String systemKey, String defaultDateFormatStr, String recentDateFormatStr, + String serverLanguageCode, String shortMonthNames, String serverTimeZoneId, + boolean lenientFutureDates, boolean saveUnparseableEntries) { + this(systemKey); + this.defaultDateFormatStr = defaultDateFormatStr; + this.lenientFutureDates = lenientFutureDates; + this.recentDateFormatStr = recentDateFormatStr; + this.saveUnparseableEntries = saveUnparseableEntries; + this.serverLanguageCode = serverLanguageCode; + this.shortMonthNames = shortMonthNames; + this.serverTimeZoneId = serverTimeZoneId; + } + + // Copy constructor, intended for use by FTPClient only + FTPClientConfig(String systemKey, FTPClientConfig config) { + this.serverSystemKey = systemKey; + this.defaultDateFormatStr = config.defaultDateFormatStr; + this.lenientFutureDates = config.lenientFutureDates; + this.recentDateFormatStr = config.recentDateFormatStr; + this.saveUnparseableEntries = config.saveUnparseableEntries; + this.serverLanguageCode = config.serverLanguageCode; + this.serverTimeZoneId = config.serverTimeZoneId; + this.shortMonthNames = config.shortMonthNames; + } + + /** + * Copy constructor + * + * @param config source + * @since 3.6 + */ + public FTPClientConfig(FTPClientConfig config) { + this.serverSystemKey = config.serverSystemKey; + this.defaultDateFormatStr = config.defaultDateFormatStr; + this.lenientFutureDates = config.lenientFutureDates; + this.recentDateFormatStr = config.recentDateFormatStr; + this.saveUnparseableEntries = config.saveUnparseableEntries; + this.serverLanguageCode = config.serverLanguageCode; + this.serverTimeZoneId = config.serverTimeZoneId; + this.shortMonthNames = config.shortMonthNames; + } + + private static final Map LANGUAGE_CODE_MAP = new TreeMap(); + + static { + + // if there are other commonly used month name encodings which + // correspond to particular locales, please add them here. + + // many locales code short names for months as all three letters + // these we handle simply. + LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); + LANGUAGE_CODE_MAP.put("de", Locale.GERMAN); + LANGUAGE_CODE_MAP.put("it", Locale.ITALIAN); + LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish + LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese + LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish + LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish + LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian + LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch + LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian + LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian + LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian + LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak + LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian + + // some don't + LANGUAGE_CODE_MAP.put("fr", + "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french + } + + /** + * Getter for the serverSystemKey property. This property + * specifies the general type of server to which the client connects. + * Should be either one of the FTPClientConfig.SYST_* codes + * or else the fully qualified class name of a parser implementing both + * the FTPFileEntryParser and Configurable + * interfaces. + * + * @return Returns the serverSystemKey property. + */ + public String getServerSystemKey() { + return serverSystemKey; + } + + /** + * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * property. + * + * @return Returns the defaultDateFormatStr property. + */ + public String getDefaultDateFormatStr() { + return defaultDateFormatStr; + } + + /** + * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property. + * + * @return Returns the recentDateFormatStr property. + */ + + public String getRecentDateFormatStr() { + return recentDateFormatStr; + } + + /** + * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property. + * + * @return Returns the serverTimeZoneId property. + */ + public String getServerTimeZoneId() { + return serverTimeZoneId; + } + + /** + *

+ * getter for the {@link #setShortMonthNames(String) shortMonthNames} + * property. + *

+ * + * @return Returns the shortMonthNames. + */ + public String getShortMonthNames() { + return shortMonthNames; + } + + /** + *

+ * getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. + *

+ * + * @return Returns the serverLanguageCode property. + */ + public String getServerLanguageCode() { + return serverLanguageCode; + } + + /** + *

+ * getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property. + *

+ * + * @return Returns the lenientFutureDates. + * @since 1.5 + */ + public boolean isLenientFutureDates() { + return lenientFutureDates; + } + + /** + *

+ * setter for the defaultDateFormatStr property. This property + * specifies the main date format that will be used by a parser configured + * by this configuration to parse file timestamps. If this is not + * specified, such a parser will use as a default value, the most commonly + * used format which will be in as used in en_US locales. + *

+ * This should be in the format described for + * java.text.SimpleDateFormat. + * property. + *

+ * + * @param defaultDateFormatStr The defaultDateFormatStr to set. + */ + public void setDefaultDateFormatStr(String defaultDateFormatStr) { + this.defaultDateFormatStr = defaultDateFormatStr; + } + + /** + *

+ * setter for the recentDateFormatStr property. This property + * specifies a secondary date format that will be used by a parser + * configured by this configuration to parse file timestamps, typically + * those less than a year old. If this is not specified, such a parser + * will not attempt to parse using an alternate format. + *

+ *

+ * This is used primarily in unix-based systems. + *

+ *

+ * This should be in the format described for + * java.text.SimpleDateFormat. + *

+ * + * @param recentDateFormatStr The recentDateFormatStr to set. + */ + public void setRecentDateFormatStr(String recentDateFormatStr) { + this.recentDateFormatStr = recentDateFormatStr; + } + + /** + *

+ * setter for the lenientFutureDates property. This boolean property + * (default: false) only has meaning when a + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} property + * has been set. In that case, if this property is set true, then the + * parser, when it encounters a listing parseable with the recent date + * format, will only consider a date to belong to the previous year if + * it is more than one day in the future. This will allow all + * out-of-synch situations (whether based on "slop" - i.e. servers simply + * out of synch with one another or because of time zone differences - + * but in the latter case it is highly recommended to use the + * {@link #setServerTimeZoneId(String) serverTimeZoneId} property + * instead) to resolve correctly. + *

+ * This is used primarily in unix-based systems. + *

+ * + * @param lenientFutureDates set true to compensate for out-of-synch + * conditions. + */ + public void setLenientFutureDates(boolean lenientFutureDates) { + this.lenientFutureDates = lenientFutureDates; + } + + /** + *

+ * setter for the serverTimeZoneId property. This property + * allows a time zone to be specified corresponding to that known to be + * used by an FTP server in file listings. This might be particularly + * useful to clients such as Ant that try to use these timestamps for + * dependency checking. + *

+ * This should be one of the identifiers used by + * java.util.TimeZone to refer to time zones, for example, + * America/Chicago or Asia/Rangoon. + *

+ * + * @param serverTimeZoneId The serverTimeZoneId to set. + */ + public void setServerTimeZoneId(String serverTimeZoneId) { + this.serverTimeZoneId = serverTimeZoneId; + } + + /** + *

+ * setter for the shortMonthNames property. + * This property allows the user to specify a set of month names + * used by the server that is different from those that may be + * specified using the {@link #setServerLanguageCode(String) serverLanguageCode} + * property. + *

+ * This should be a string containing twelve strings each composed of + * three characters, delimited by pipe (|) characters. Currently, + * only 8-bit ASCII characters are known to be supported. For example, + * a set of month names used by a hypothetical Icelandic FTP server might + * conceivably be specified as + * "jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des". + *

+ * + * @param shortMonthNames The value to set to the shortMonthNames property. + */ + public void setShortMonthNames(String shortMonthNames) { + this.shortMonthNames = shortMonthNames; + } + + /** + *

+ * setter for the serverLanguageCode property. This property allows + * user to specify a + * + * two-letter ISO-639 language code that will be used to + * configure the set of month names used by the file timestamp parser. + * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} + * is specified, parsing will assume English month names, which may or + * may not be significant, depending on whether the date format(s) + * specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using + * numeric or alphabetic month names. + *

+ *

If the code supplied is not supported here, en_US + * month names will be used. We are supporting here those language + * codes which, when a java.util.Locale is constucted + * using it, and a java.text.SimpleDateFormat is + * constructed using that Locale, the array returned by the + * SimpleDateFormat's getShortMonths() method consists + * solely of three 8-bit ASCII character strings. Additionally, + * languages which do not meet this requirement are included if a + * common alternative set of short month names is known to be used. + * This means that users who can tell us of additional such encodings + * may get them added to the list of supported languages by contacting + * the Apache Commons Net team. + *

+ *

+ * Please note that this attribute will NOT be used to determine a + * locale-based date format for the language. + * Experience has shown that many if not most FTP servers outside the + * United States employ the standard en_US date format + * orderings of MMM d yyyy and MMM d HH:mm + * and attempting to deduce this automatically here would cause more + * problems than it would solve. The date format must be changed + * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters. + *

+ * + * @param serverLanguageCode The value to set to the serverLanguageCode property. + */ + public void setServerLanguageCode(String serverLanguageCode) { + this.serverLanguageCode = serverLanguageCode; + } + + /** + * Looks up the supplied language code in the internally maintained table of + * language codes. Returns a DateFormatSymbols object configured with + * short month names corresponding to the code. If there is no corresponding + * entry in the table, the object returned will be that for + * Locale.US + * + * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} + * @return a DateFormatSymbols object configured with short month names + * corresponding to the supplied code, or with month names for + * Locale.US if there is no corresponding entry in the internal + * table. + */ + public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) { + Object lang = LANGUAGE_CODE_MAP.get(languageCode); + if (lang != null) { + if (lang instanceof Locale) { + return new DateFormatSymbols((Locale) lang); + } else if (lang instanceof String) { + return getDateFormatSymbols((String) lang); + } + } + return new DateFormatSymbols(Locale.US); + } + + /** + * Returns a DateFormatSymbols object configured with short month names + * as in the supplied string + * + * @param shortmonths This should be as described in + * {@link #setShortMonthNames(String) shortMonthNames} + * @return a DateFormatSymbols object configured with short month names + * as in the supplied string + */ + public static DateFormatSymbols getDateFormatSymbols(String shortmonths) { + String[] months = splitShortMonthString(shortmonths); + DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); + dfs.setShortMonths(months); + return dfs; + } + + private static String[] splitShortMonthString(String shortmonths) { + StringTokenizer st = new StringTokenizer(shortmonths, "|"); + int monthcnt = st.countTokens(); + if (12 != monthcnt) { + throw new IllegalArgumentException("expecting a pipe-delimited string containing 12 tokens"); + } + String[] months = new String[13]; + int pos = 0; + while (st.hasMoreTokens()) { + months[pos++] = st.nextToken(); + } + months[pos] = ""; + return months; + } + + /** + * Returns a Collection of all the language codes currently supported + * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} + * for a functional descrption of language codes within this system. + * + * @return a Collection of all the language codes currently supported + * by this class + */ + public static Collection getSupportedLanguageCodes() { + return LANGUAGE_CODE_MAP.keySet(); + } + + /** + * Allow list parsing methods to create basic FTPFile entries if parsing fails. + *

+ * In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()} + * and {@link FTPFile#isValid()} will return {@code false} + * + * @param saveUnparseable if true, then create FTPFile entries if parsing fails + * @since 3.4 + */ + public void setUnparseableEntries(boolean saveUnparseable) { + this.saveUnparseableEntries = saveUnparseable; + } + + /** + * @return true if list parsing should return FTPFile entries even for unparseable response lines + *

+ * If true, the FTPFile for any unparseable entries will contain only the unparsed entry + * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false} + * @since 3.4 + */ + public boolean getUnparseableEntries() { + return this.saveUnparseableEntries; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java new file mode 100644 index 00000000..de14835b --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.ftp; + +/** + * @since 3.3 + */ +public enum FTPCmd { + ABOR, ACCT, ALLO, APPE, CDUP, CWD, DELE, EPRT, EPSV, FEAT, HELP, LIST, MDTM, MFMT, MKD, MLSD, MLST, MODE, NLST, NOOP, PASS, PASV, PORT, PWD, QUIT, REIN, REST, RETR, RMD, RNFR, RNTO, SITE, SMNT, STAT, STOR, STOU, STRU, SYST, TYPE, USER,; + + // Aliases + + public static final FTPCmd ABORT = ABOR; + public static final FTPCmd ACCOUNT = ACCT; + public static final FTPCmd ALLOCATE = ALLO; + public static final FTPCmd APPEND = APPE; + public static final FTPCmd CHANGE_TO_PARENT_DIRECTORY = CDUP; + public static final FTPCmd CHANGE_WORKING_DIRECTORY = CWD; + public static final FTPCmd DATA_PORT = PORT; + public static final FTPCmd DELETE = DELE; + public static final FTPCmd FEATURES = FEAT; + public static final FTPCmd FILE_STRUCTURE = STRU; + public static final FTPCmd GET_MOD_TIME = MDTM; + public static final FTPCmd LOGOUT = QUIT; + public static final FTPCmd MAKE_DIRECTORY = MKD; + public static final FTPCmd MOD_TIME = MDTM; + public static final FTPCmd NAME_LIST = NLST; + public static final FTPCmd PASSIVE = PASV; + public static final FTPCmd PASSWORD = PASS; + public static final FTPCmd PRINT_WORKING_DIRECTORY = PWD; + public static final FTPCmd REINITIALIZE = REIN; + public static final FTPCmd REMOVE_DIRECTORY = RMD; + public static final FTPCmd RENAME_FROM = RNFR; + public static final FTPCmd RENAME_TO = RNTO; + public static final FTPCmd REPRESENTATION_TYPE = TYPE; + public static final FTPCmd RESTART = REST; + public static final FTPCmd RETRIEVE = RETR; + public static final FTPCmd SET_MOD_TIME = MFMT; + public static final FTPCmd SITE_PARAMETERS = SITE; + public static final FTPCmd STATUS = STAT; + public static final FTPCmd STORE = STOR; + public static final FTPCmd STORE_UNIQUE = STOU; + public static final FTPCmd STRUCTURE_MOUNT = SMNT; + public static final FTPCmd SYSTEM = SYST; + public static final FTPCmd TRANSFER_MODE = MODE; + public static final FTPCmd USERNAME = USER; + + /** + * Retrieve the FTP protocol command string corresponding to a specified + * command code. + * + * @return The FTP protcol command string corresponding to a specified + * command code. + */ + public final String getCommand() { + return this.name(); + } + +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java new file mode 100644 index 00000000..3a1aef89 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +/** + * FTPCommand stores a set of constants for FTP command codes. To interpret + * the meaning of the codes, familiarity with RFC 959 is assumed. + * The mnemonic constant names are transcriptions from the code descriptions + * of RFC 959. For those who think in terms of the actual FTP commands, + * a set of constants such as {@link #USER USER } are provided + * where the constant name is the same as the FTP command. + * + * @deprecated use {@link FTPCmd} instead + */ +@Deprecated public final class FTPCommand { + + public static final int USER = 0; + public static final int PASS = 1; + public static final int ACCT = 2; + public static final int CWD = 3; + public static final int CDUP = 4; + public static final int SMNT = 5; + public static final int REIN = 6; + public static final int QUIT = 7; + public static final int PORT = 8; + public static final int PASV = 9; + public static final int TYPE = 10; + public static final int STRU = 11; + public static final int MODE = 12; + public static final int RETR = 13; + public static final int STOR = 14; + public static final int STOU = 15; + public static final int APPE = 16; + public static final int ALLO = 17; + public static final int REST = 18; + public static final int RNFR = 19; + public static final int RNTO = 20; + public static final int ABOR = 21; + public static final int DELE = 22; + public static final int RMD = 23; + public static final int MKD = 24; + public static final int PWD = 25; + public static final int LIST = 26; + public static final int NLST = 27; + public static final int SITE = 28; + public static final int SYST = 29; + public static final int STAT = 30; + public static final int HELP = 31; + public static final int NOOP = 32; + /** @since 2.0 */ + public static final int MDTM = 33; + /** @since 2.2 */ + public static final int FEAT = 34; + /** @since 2.2 */ + public static final int MFMT = 35; + /** @since 2.2 */ + public static final int EPSV = 36; + /** @since 2.2 */ + public static final int EPRT = 37; + + /** + * Machine parseable list for a directory + * + * @since 3.0 + */ + public static final int MLSD = 38; + + /** + * Machine parseable list for a single file + * + * @since 3.0 + */ + public static final int MLST = 39; + + // Must agree with final entry above; used to check array size + private static final int LAST = MLST; + + public static final int USERNAME = USER; + public static final int PASSWORD = PASS; + public static final int ACCOUNT = ACCT; + public static final int CHANGE_WORKING_DIRECTORY = CWD; + public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP; + public static final int STRUCTURE_MOUNT = SMNT; + public static final int REINITIALIZE = REIN; + public static final int LOGOUT = QUIT; + public static final int DATA_PORT = PORT; + public static final int PASSIVE = PASV; + public static final int REPRESENTATION_TYPE = TYPE; + public static final int FILE_STRUCTURE = STRU; + public static final int TRANSFER_MODE = MODE; + public static final int RETRIEVE = RETR; + public static final int STORE = STOR; + public static final int STORE_UNIQUE = STOU; + public static final int APPEND = APPE; + public static final int ALLOCATE = ALLO; + public static final int RESTART = REST; + public static final int RENAME_FROM = RNFR; + public static final int RENAME_TO = RNTO; + public static final int ABORT = ABOR; + public static final int DELETE = DELE; + public static final int REMOVE_DIRECTORY = RMD; + public static final int MAKE_DIRECTORY = MKD; + public static final int PRINT_WORKING_DIRECTORY = PWD; + // public static final int LIST = LIST; + public static final int NAME_LIST = NLST; + public static final int SITE_PARAMETERS = SITE; + public static final int SYSTEM = SYST; + public static final int STATUS = STAT; + //public static final int HELP = HELP; + //public static final int NOOP = NOOP; + + /** @since 2.0 */ + public static final int MOD_TIME = MDTM; + + /** @since 2.2 */ + public static final int FEATURES = FEAT; + /** @since 2.2 */ + public static final int GET_MOD_TIME = MDTM; + /** @since 2.2 */ + public static final int SET_MOD_TIME = MFMT; + + // Cannot be instantiated + private FTPCommand() { + } + + private static final String[] _commands = { + "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT", "PASV", "TYPE", "STRU", + "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO", "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", + "MKD", "PWD", "LIST", "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP", "MDTM", "FEAT", "MFMT", + "EPSV", "EPRT", "MLSD", "MLST" + }; + + // default access needed for Unit test + static void checkArray() { + int expectedLength = LAST + 1; + if (_commands.length != expectedLength) { + throw new RuntimeException("Incorrect _commands array. Should have length " + + expectedLength + + " found " + + _commands.length); + } + } + + /** + * Retrieve the FTP protocol command string corresponding to a specified + * command code. + * + * @param command The command code. + * @return The FTP protcol command string corresponding to a specified + * command code. + */ + public static final String getCommand(int command) { + return _commands[command]; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java new file mode 100644 index 00000000..aa95277e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.IOException; + +/*** + * FTPConnectionClosedException is used to indicate the premature or + * unexpected closing of an FTP connection resulting from a + * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } + * response (FTP reply code 421) to a + * failed FTP command. This exception is derived from IOException and + * therefore may be caught either as an IOException or specifically as an + * FTPConnectionClosedException. + * + * @see FTP + * @see FTPClient + ***/ + +public class FTPConnectionClosedException extends IOException { + + private static final long serialVersionUID = 3500547241659379952L; + + /*** Constructs a FTPConnectionClosedException with no message ***/ + public FTPConnectionClosedException() { + super(); + } + + /*** + * Constructs a FTPConnectionClosedException with a specified message. + * + * @param message The message explaining the reason for the exception. + ***/ + public FTPConnectionClosedException(String message) { + super(message); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java new file mode 100644 index 00000000..5edcc119 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java @@ -0,0 +1,493 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.Formatter; +import java.util.TimeZone; + +/*** + * The FTPFile class is used to represent information about files stored + * on an FTP server. + * + * @see FTPFileEntryParser + * @see FTPClient#listFiles + ***/ + +public class FTPFile implements Serializable { + private static final long serialVersionUID = 9010790363003271996L; + + /** A constant indicating an FTPFile is a file. ***/ + public static final int FILE_TYPE = 0; + /** A constant indicating an FTPFile is a directory. ***/ + public static final int DIRECTORY_TYPE = 1; + /** A constant indicating an FTPFile is a symbolic link. ***/ + public static final int SYMBOLIC_LINK_TYPE = 2; + /** A constant indicating an FTPFile is of unknown type. ***/ + public static final int UNKNOWN_TYPE = 3; + + /** A constant indicating user access permissions. ***/ + public static final int USER_ACCESS = 0; + /** A constant indicating group access permissions. ***/ + public static final int GROUP_ACCESS = 1; + /** A constant indicating world access permissions. ***/ + public static final int WORLD_ACCESS = 2; + + /** A constant indicating file/directory read permission. ***/ + public static final int READ_PERMISSION = 0; + /** A constant indicating file/directory write permission. ***/ + public static final int WRITE_PERMISSION = 1; + /** + * A constant indicating file execute permission or directory listing + * permission. + ***/ + public static final int EXECUTE_PERMISSION = 2; + + private int _type, _hardLinkCount; + private long _size; + private String _rawListing, _user, _group, _name, _link; + private Calendar _date; + // If this is null, then list entry parsing failed + private final boolean[] _permissions[]; // e.g. _permissions[USER_ACCESS][READ_PERMISSION] + + /*** Creates an empty FTPFile. ***/ + public FTPFile() { + _permissions = new boolean[3][3]; + _type = UNKNOWN_TYPE; + // init these to values that do not occur in listings + // so can distinguish which fields are unset + _hardLinkCount = 0; // 0 is invalid as a link count + _size = -1; // 0 is valid, so use -1 + _user = ""; + _group = ""; + _date = null; + _name = null; + } + + /** + * Constructor for use by {@link FTPListParseEngine} only. + * Used to create FTPFile entries for failed parses + * + * @param rawListing line that could not be parsed. + * @since 3.4 + */ + FTPFile(String rawListing) { + _permissions = null; // flag that entry is invalid + _rawListing = rawListing; + _type = UNKNOWN_TYPE; + // init these to values that do not occur in listings + // so can distinguish which fields are unset + _hardLinkCount = 0; // 0 is invalid as a link count + _size = -1; // 0 is valid, so use -1 + _user = ""; + _group = ""; + _date = null; + _name = null; + } + + /*** + * Set the original FTP server raw listing from which the FTPFile was + * created. + * + * @param rawListing The raw FTP server listing. + ***/ + public void setRawListing(String rawListing) { + _rawListing = rawListing; + } + + /*** + * Get the original FTP server raw listing used to initialize the FTPFile. + * + * @return The original FTP server raw listing used to initialize the + * FTPFile. + ***/ + public String getRawListing() { + return _rawListing; + } + + /*** + * Determine if the file is a directory. + * + * @return True if the file is of type DIRECTORY_TYPE, false if + * not. + ***/ + public boolean isDirectory() { + return (_type == DIRECTORY_TYPE); + } + + /*** + * Determine if the file is a regular file. + * + * @return True if the file is of type FILE_TYPE, false if + * not. + ***/ + public boolean isFile() { + return (_type == FILE_TYPE); + } + + /*** + * Determine if the file is a symbolic link. + * + * @return True if the file is of type UNKNOWN_TYPE, false if + * not. + ***/ + public boolean isSymbolicLink() { + return (_type == SYMBOLIC_LINK_TYPE); + } + + /*** + * Determine if the type of the file is unknown. + * + * @return True if the file is of type UNKNOWN_TYPE, false if + * not. + ***/ + public boolean isUnknown() { + return (_type == UNKNOWN_TYPE); + } + + /** + * Used to indicate whether an entry is valid or not. + * If the entry is invalid, only the {@link #getRawListing()} method will be useful. + * Other methods may fail. + * + * Used in conjunction with list parsing that preseverves entries that failed to parse. + * + * @return true if the entry is valid + * @see FTPClientConfig#setUnparseableEntries(boolean) + * @since 3.4 + */ + public boolean isValid() { + return (_permissions != null); + } + + /*** + * Set the type of the file (DIRECTORY_TYPE, + * FILE_TYPE, etc.). + * + * @param type The integer code representing the type of the file. + ***/ + public void setType(int type) { + _type = type; + } + + /*** + * Return the type of the file (one of the _TYPE constants), + * e.g., if it is a directory, a regular file, or a symbolic link. + * + * @return The type of the file. + ***/ + public int getType() { + return _type; + } + + /*** + * Set the name of the file. + * + * @param name The name of the file. + ***/ + public void setName(String name) { + _name = name; + } + + /*** + * Return the name of the file. + * + * @return The name of the file. + ***/ + public String getName() { + return _name; + } + + /** + * Set the file size in bytes. + * + * @param size The file size in bytes. + */ + public void setSize(long size) { + _size = size; + } + + /*** + * Return the file size in bytes. + * + * @return The file size in bytes. + ***/ + public long getSize() { + return _size; + } + + /*** + * Set the number of hard links to this file. This is not to be + * confused with symbolic links. + * + * @param links The number of hard links to this file. + ***/ + public void setHardLinkCount(int links) { + _hardLinkCount = links; + } + + /*** + * Return the number of hard links to this file. This is not to be + * confused with symbolic links. + * + * @return The number of hard links to this file. + ***/ + public int getHardLinkCount() { + return _hardLinkCount; + } + + /*** + * Set the name of the group owning the file. This may be + * a string representation of the group number. + * + * @param group The name of the group owning the file. + ***/ + public void setGroup(String group) { + _group = group; + } + + /*** + * Returns the name of the group owning the file. Sometimes this will be + * a string representation of the group number. + * + * @return The name of the group owning the file. + ***/ + public String getGroup() { + return _group; + } + + /*** + * Set the name of the user owning the file. This may be + * a string representation of the user number; + * + * @param user The name of the user owning the file. + ***/ + public void setUser(String user) { + _user = user; + } + + /*** + * Returns the name of the user owning the file. Sometimes this will be + * a string representation of the user number. + * + * @return The name of the user owning the file. + ***/ + public String getUser() { + return _user; + } + + /*** + * If the FTPFile is a symbolic link, use this method to set the name of the + * file being pointed to by the symbolic link. + * + * @param link The file pointed to by the symbolic link. + ***/ + public void setLink(String link) { + _link = link; + } + + /*** + * If the FTPFile is a symbolic link, this method returns the name of the + * file being pointed to by the symbolic link. Otherwise it returns null. + * + * @return The file pointed to by the symbolic link (null if the FTPFile + * is not a symbolic link). + ***/ + public String getLink() { + return _link; + } + + /*** + * Set the file timestamp. This usually the last modification time. + * The parameter is not cloned, so do not alter its value after calling + * this method. + * + * @param date A Calendar instance representing the file timestamp. + ***/ + public void setTimestamp(Calendar date) { + _date = date; + } + + /*** + * Returns the file timestamp. This usually the last modification time. + * + * @return A Calendar instance representing the file timestamp. + ***/ + public Calendar getTimestamp() { + return _date; + } + + /*** + * Set if the given access group (one of the _ACCESS + * constants) has the given access permission (one of the + * _PERMISSION constants) to the file. + * + * @param access The access group (one of the _ACCESS + * constants) + * @param permission The access permission (one of the + * _PERMISSION constants) + * @param value True if permission is allowed, false if not. + * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range + ***/ + public void setPermission(int access, int permission, boolean value) { + _permissions[access][permission] = value; + } + + /*** + * Determines if the given access group (one of the _ACCESS + * constants) has the given access permission (one of the + * _PERMISSION constants) to the file. + * + * @param access The access group (one of the _ACCESS + * constants) + * @param permission The access permission (one of the + * _PERMISSION constants) + * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range + * @return true if {@link #isValid()} is {@code true &&} the associated permission is set; + * {@code false} otherwise. + ***/ + public boolean hasPermission(int access, int permission) { + if (_permissions == null) { + return false; + } + return _permissions[access][permission]; + } + + /*** + * Returns a string representation of the FTPFile information. + * + * @return A string representation of the FTPFile information. + */ + @Override public String toString() { + return getRawListing(); + } + + /*** + * Returns a string representation of the FTPFile information. + * This currently mimics the Unix listing format. + * This method uses the timezone of the Calendar entry, which is + * the server time zone (if one was provided) otherwise it is + * the local time zone. + *

+ * Note: if the instance is not valid {@link #isValid()}, no useful + * information can be returned. In this case, use {@link #getRawListing()} + * instead. + * + * @return A string representation of the FTPFile information. + * @since 3.0 + */ + public String toFormattedString() { + return toFormattedString(null); + } + + /** + * Returns a string representation of the FTPFile information. + * This currently mimics the Unix listing format. + * This method allows the Calendar time zone to be overridden. + *

+ * Note: if the instance is not valid {@link #isValid()}, no useful + * information can be returned. In this case, use {@link #getRawListing()} + * instead. + * + * @param timezone the timezone to use for displaying the time stamp + * If {@code null}, then use the Calendar entry timezone + * @return A string representation of the FTPFile information. + * @since 3.4 + */ + public String toFormattedString(final String timezone) { + + if (!isValid()) { + return "[Invalid: could not parse file entry]"; + } + StringBuilder sb = new StringBuilder(); + Formatter fmt = new Formatter(sb); + sb.append(formatType()); + sb.append(permissionToString(USER_ACCESS)); + sb.append(permissionToString(GROUP_ACCESS)); + sb.append(permissionToString(WORLD_ACCESS)); + fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); + fmt.format(" %-8s %-8s", getUser(), getGroup()); + fmt.format(" %8d", Long.valueOf(getSize())); + Calendar timestamp = getTimestamp(); + if (timestamp != null) { + if (timezone != null) { + TimeZone newZone = TimeZone.getTimeZone(timezone); + if (!newZone.equals(timestamp.getTimeZone())) { + Date original = timestamp.getTime(); + Calendar newStamp = Calendar.getInstance(newZone); + newStamp.setTime(original); + timestamp = newStamp; + } + } + fmt.format(" %1$tY-%1$tm-%1$td", timestamp); + // Only display time units if they are present + if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { + fmt.format(" %1$tH", timestamp); + if (timestamp.isSet(Calendar.MINUTE)) { + fmt.format(":%1$tM", timestamp); + if (timestamp.isSet(Calendar.SECOND)) { + fmt.format(":%1$tS", timestamp); + if (timestamp.isSet(Calendar.MILLISECOND)) { + fmt.format(".%1$tL", timestamp); + } + } + } + fmt.format(" %1$tZ", timestamp); + } + } + sb.append(' '); + sb.append(getName()); + fmt.close(); + return sb.toString(); + } + + private char formatType() { + switch (_type) { + case FILE_TYPE: + return '-'; + case DIRECTORY_TYPE: + return 'd'; + case SYMBOLIC_LINK_TYPE: + return 'l'; + default: + return '?'; + } + } + + private String permissionToString(int access) { + StringBuilder sb = new StringBuilder(); + if (hasPermission(access, READ_PERMISSION)) { + sb.append('r'); + } else { + sb.append('-'); + } + if (hasPermission(access, WRITE_PERMISSION)) { + sb.append('w'); + } else { + sb.append('-'); + } + if (hasPermission(access, EXECUTE_PERMISSION)) { + sb.append('x'); + } else { + sb.append('-'); + } + return sb.toString(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java new file mode 100644 index 00000000..0c04b265 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.List; + +/** + * FTPFileEntryParser defines the interface for parsing a single FTP file + * listing and converting that information into an + * {@link FTPFile} instance. + * Sometimes you will want to parse unusual listing formats, in which + * case you would create your own implementation of FTPFileEntryParser and + * if necessary, subclass FTPFile. + *

+ * Here are some examples showing how to use one of the classes that + * implement this interface. + *

+ * + * The first example uses the FTPClient.listFiles() + * API to pull the whole list from the subfolder subfolder in + * one call, attempting to automatically detect the parser type. This + * method, without a parserKey parameter, indicates that autodection should + * be used. + * + *

+ *    FTPClient f=FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = f.listFiles("subfolder");
+ * 
+ * + * The second example uses the FTPClient.listFiles() + * API to pull the whole list from the current working directory in one call, + * but specifying by classname the parser to be used. For this particular + * parser class, this approach is necessary since there is no way to + * autodetect this server type. + * + *
+ *    FTPClient f=FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = f.listFiles(
+ *      "org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser",
+ *      ".");
+ * 
+ * + * The third example uses the FTPClient.listFiles() + * API to pull a single file listing in an arbitrary directory in one call, + * specifying by KEY the parser to be used, in this case, VMS. + * + *
+ *    FTPClient f=FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java");
+ * 
+ * + * For an alternative approach, see the {@link FTPListParseEngine} class + * which provides iterative access. + * + * @version $Id: FTPFileEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ + * @see FTPFile + * @see FTPClient#listFiles() + */ +public interface FTPFileEntryParser { + /** + * Parses a line of an FTP server file listing and converts it into a usable + * format in the form of an FTPFile instance. If the + * file listing line doesn't describe a file, null should be + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + * + * @param listEntry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + FTPFile parseFTPEntry(String listEntry); + + /** + * Reads the next entry using the supplied BufferedReader object up to + * whatever delemits one entry from the next. Implementors must define + * this for the particular ftp system being parsed. In many but not all + * cases, this can be defined simply by calling BufferedReader.readLine(). + * + * @param reader The BufferedReader object from which entries are to be + * read. + * @return A string representing the next ftp entry or null if none found. + * @throws IOException thrown on any IO Error reading from the reader. + */ + String readNextEntry(BufferedReader reader) throws IOException; + + /** + * This method is a hook for those implementors (such as + * VMSVersioningFTPEntryParser, and possibly others) which need to + * perform some action upon the FTPFileList after it has been created + * from the server stream, but before any clients see the list. + * + * The default implementation can be a no-op. + * + * @param original Original list after it has been created from the server stream + * @return Original list as processed by this method. + */ + List preParse(List original); +} + + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java new file mode 100644 index 00000000..5af13643 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.List; + +/** + * This abstract class implements both the older FTPFileListParser and + * newer FTPFileEntryParser interfaces with default functionality. + * All the classes in the parser subpackage inherit from this. + */ +public abstract class FTPFileEntryParserImpl implements FTPFileEntryParser { + /** + * The constructor for a FTPFileEntryParserImpl object. + */ + public FTPFileEntryParserImpl() { + } + + /** + * Reads the next entry using the supplied BufferedReader object up to + * whatever delimits one entry from the next. This default implementation + * simply calls BufferedReader.readLine(). + * + * @param reader The BufferedReader object from which entries are to be + * read. + * @return A string representing the next ftp entry or null if none found. + * @throws IOException thrown on any IO Error reading from the reader. + */ + @Override public String readNextEntry(BufferedReader reader) throws IOException { + return reader.readLine(); + } + + /** + * This method is a hook for those implementors (such as + * VMSVersioningFTPEntryParser, and possibly others) which need to + * perform some action upon the FTPFileList after it has been created + * from the server stream, but before any clients see the list. + * + * This default implementation does nothing. + * + * @param original Original list after it has been created from the server stream + * @return original unmodified. + */ + @Override public List preParse(List original) { + return original; + } +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java new file mode 100644 index 00000000..898ee154 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.ftp; + +/** + * Perform filtering on FTPFile entries. + * + * @since 2.2 + */ +public interface FTPFileFilter { + /** + * Checks if an FTPFile entry should be included or not. + * + * @param file entry to be checked for inclusion. May be null. + * @return true if the file is to be included, false otherwise + */ + public boolean accept(FTPFile file); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java new file mode 100644 index 00000000..40a59a99 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.ftp; + +/** + * Implements some simple FTPFileFilter classes. + * + * @since 2.2 + */ +public class FTPFileFilters { + + /** + * Accepts all FTPFile entries, including null. + */ + public static final FTPFileFilter ALL = new FTPFileFilter() { + @Override public boolean accept(FTPFile file) { + return true; + } + }; + + /** + * Accepts all non-null FTPFile entries. + */ + public static final FTPFileFilter NON_NULL = new FTPFileFilter() { + @Override public boolean accept(FTPFile file) { + return file != null; + } + }; + + /** + * Accepts all (non-null) FTPFile directory entries. + */ + public static final FTPFileFilter DIRECTORIES = new FTPFileFilter() { + @Override public boolean accept(FTPFile file) { + return file != null && file.isDirectory(); + } + }; +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java new file mode 100644 index 00000000..0304e427 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.Inet6Address; +import java.net.Socket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +import aria.apache.commons.net.util.Base64; + +/** + * Experimental attempt at FTP client that tunnels over an HTTP proxy connection. + * + * @since 2.2 + */ +public class FTPHTTPClient extends FTPClient { + private final String proxyHost; + private final int proxyPort; + private final String proxyUsername; + private final String proxyPassword; + + private static final byte[] CRLF = { '\r', '\n' }; + private final Base64 base64 = new Base64(); + + private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV) + + public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUsername = proxyUser; + this.proxyPassword = proxyPass; + this.tunnelHost = null; + } + + public FTPHTTPClient(String proxyHost, int proxyPort) { + this(proxyHost, proxyPort, null, null); + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if connection mode is not passive + * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead + */ + // Kept to maintain binary compatibility + // Not strictly necessary, but Clirr complains even though there is a super-impl + @Override @Deprecated protected Socket _openDataConnection_(int command, String arg) + throws IOException { + return super._openDataConnection_(command, arg); + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if connection mode is not passive + * @since 3.1 + */ + @Override protected Socket _openDataConnection_(String command, String arg) throws IOException { + //Force local passive mode, active mode not supported by through proxy + if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { + throw new IllegalStateException("Only passive connection mode supported"); + } + + final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; + String passiveHost = null; + + boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; + if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { + _parseExtendedPassiveModeReply(_replyLines.get(0)); + passiveHost = this.tunnelHost; + } else { + if (isInet6Address) { + return null; // Must use EPSV for IPV6 + } + // If EPSV failed on IPV4, revert to PASV + if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { + return null; + } + _parsePassiveModeReply(_replyLines.get(0)); + passiveHost = this.getPassiveHost(); + } + + Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort); + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + tunnelHandshake(passiveHost, this.getPassivePort(), is, os); + if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { + socket.close(); + return null; + } + + if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { + socket.close(); + return null; + } + + return socket; + } + + @Override public void connect(String host, int port) throws SocketException, IOException { + + _socket_ = _socketFactory_.createSocket(proxyHost, proxyPort); + _input_ = _socket_.getInputStream(); + _output_ = _socket_.getOutputStream(); + Reader socketIsReader; + try { + socketIsReader = tunnelHandshake(host, port, _input_, _output_); + } catch (Exception e) { + IOException ioe = new IOException("Could not connect to " + host + " using port " + port); + ioe.initCause(e); + throw ioe; + } + super._connectAction_(socketIsReader); + } + + private BufferedReader tunnelHandshake(String host, int port, InputStream input, + OutputStream output) throws IOException, UnsupportedEncodingException { + final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1"; + final String hostString = "Host: " + host + ":" + port; + + this.tunnelHost = host; + output.write(connectString.getBytes("UTF-8")); // TODO what is the correct encoding? + output.write(CRLF); + output.write(hostString.getBytes("UTF-8")); + output.write(CRLF); + + if (proxyUsername != null && proxyPassword != null) { + final String auth = proxyUsername + ":" + proxyPassword; + final String header = + "Proxy-Authorization: Basic " + base64.encodeToString(auth.getBytes("UTF-8")); + output.write(header.getBytes("UTF-8")); + } + output.write(CRLF); + + List response = new ArrayList(); + BufferedReader reader = new BufferedReader(new InputStreamReader(input, getCharset())); + + for (String line = reader.readLine(); line != null && line.length() > 0; + line = reader.readLine()) { + response.add(line); + } + + int size = response.size(); + if (size == 0) { + throw new IOException("No response from proxy"); + } + + String code = null; + String resp = response.get(0); + if (resp.startsWith("HTTP/") && resp.length() >= 12) { + code = resp.substring(9, 12); + } else { + throw new IOException("Invalid response from proxy: " + resp); + } + + if (!"200".equals(code)) { + StringBuilder msg = new StringBuilder(); + msg.append("HTTPTunnelConnector: connection failed\r\n"); + msg.append("Response received from the proxy:\r\n"); + for (String line : response) { + msg.append(line); + msg.append("\r\n"); + } + throw new IOException(msg.toString()); + } + return reader; + } +} + + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java new file mode 100644 index 00000000..51e117e5 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java @@ -0,0 +1,311 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import aria.apache.commons.net.util.Charsets; + +/** + * This class handles the entire process of parsing a listing of + * file entries from the server. + *

+ * This object defines a two-part parsing mechanism. + *

+ * The first part is comprised of reading the raw input into an internal + * list of strings. Every item in this list corresponds to an actual + * file. All extraneous matter emitted by the server will have been + * removed by the end of this phase. This is accomplished in conjunction + * with the FTPFileEntryParser associated with this engine, by calling + * its methods readNextEntry() - which handles the issue of + * what delimits one entry from another, usually but not always a line + * feed and preParse() - which handles removal of + * extraneous matter such as the preliminary lines of a listing, removal + * of duplicates on versioning systems, etc. + *

+ * The second part is composed of the actual parsing, again in conjunction + * with the particular parser used by this engine. This is controlled + * by an iterator over the internal list of strings. This may be done + * either in block mode, by calling the getNext() and + * getPrevious() methods to provide "paged" output of less + * than the whole list at one time, or by calling the + * getFiles() method to return the entire list. + *

+ * Examples: + *

+ * Paged access: + *

+ *    FTPClient f=FTPClient();
+ *    f.connect(server);
+ *    f.login(username, password);
+ *    FTPListParseEngine engine = f.initiateListParsing(directory);
+ *
+ *    while (engine.hasNext()) {
+ *       FTPFile[] files = engine.getNext(25);  // "page size" you want
+ *       //do whatever you want with these files, display them, etc.
+ *       //expensive FTPFile objects not created until needed.
+ *    }
+ * 
+ *

+ * For unpaged access, simply use FTPClient.listFiles(). That method + * uses this class transparently. + * + * @version $Id: FTPListParseEngine.java 1747119 2016-06-07 02:22:24Z ggregory $ + */ +public class FTPListParseEngine { + private List entries = new LinkedList(); + private ListIterator _internalIterator = entries.listIterator(); + + private final FTPFileEntryParser parser; + // Should invalid files (parse failures) be allowed? + private final boolean saveUnparseableEntries; + + public FTPListParseEngine(FTPFileEntryParser parser) { + this(parser, null); + } + + /** + * Intended for use by FTPClient only + * + * @since 3.4 + */ + FTPListParseEngine(FTPFileEntryParser parser, FTPClientConfig configuration) { + this.parser = parser; + if (configuration != null) { + this.saveUnparseableEntries = configuration.getUnparseableEntries(); + } else { + this.saveUnparseableEntries = false; + } + } + + /** + * handle the initial reading and preparsing of the list returned by + * the server. After this method has completed, this object will contain + * a list of unparsed entries (Strings) each referring to a unique file + * on the server. + * + * @param stream input stream provided by the server socket. + * @param encoding the encoding to be used for reading the stream + * @throws IOException thrown on any failure to read from the sever. + */ + public void readServerList(InputStream stream, String encoding) throws IOException { + this.entries = new LinkedList(); + readStream(stream, encoding); + this.parser.preParse(this.entries); + resetIterator(); + } + + /** + * Internal method for reading the input into the entries list. + * After this method has completed, entries will contain a + * collection of entries (as defined by + * FTPFileEntryParser.readNextEntry()), but this may contain + * various non-entry preliminary lines from the server output, duplicates, + * and other data that will not be part of the final listing. + * + * @param stream The socket stream on which the input will be read. + * @param encoding The encoding to use. + * @throws IOException thrown on any failure to read the stream + */ + private void readStream(InputStream stream, String encoding) throws IOException { + BufferedReader reader = + new BufferedReader(new InputStreamReader(stream, Charsets.toCharset(encoding))); + + String line = this.parser.readNextEntry(reader); + + while (line != null) { + this.entries.add(line); + line = this.parser.readNextEntry(reader); + } + reader.close(); + } + + /** + * Returns an array of at most quantityRequested FTPFile + * objects starting at this object's internal iterator's current position. + * If fewer than quantityRequested such + * elements are available, the returned array will have a length equal + * to the number of entries at and after after the current position. + * If no such entries are found, this array will have a length of 0. + * + * After this method is called this object's internal iterator is advanced + * by a number of positions equal to the size of the array returned. + * + * @param quantityRequested the maximum number of entries we want to get. + * @return an array of at most quantityRequested FTPFile + * objects starting at the current position of this iterator within its + * list and at least the number of elements which exist in the list at + * and after its current position. + *

+ * NOTE: This array may contain null members if any of the + * individual file listings failed to parse. The caller should + * check each entry for null before referencing it. + */ + public FTPFile[] getNext(int quantityRequested) { + List tmpResults = new LinkedList(); + int count = quantityRequested; + while (count > 0 && this._internalIterator.hasNext()) { + String entry = this._internalIterator.next(); + FTPFile temp = this.parser.parseFTPEntry(entry); + if (temp == null && saveUnparseableEntries) { + temp = new FTPFile(entry); + } + tmpResults.add(temp); + count--; + } + return tmpResults.toArray(new FTPFile[tmpResults.size()]); + } + + /** + * Returns an array of at most quantityRequested FTPFile + * objects starting at this object's internal iterator's current position, + * and working back toward the beginning. + * + * If fewer than quantityRequested such + * elements are available, the returned array will have a length equal + * to the number of entries at and after after the current position. + * If no such entries are found, this array will have a length of 0. + * + * After this method is called this object's internal iterator is moved + * back by a number of positions equal to the size of the array returned. + * + * @param quantityRequested the maximum number of entries we want to get. + * @return an array of at most quantityRequested FTPFile + * objects starting at the current position of this iterator within its + * list and at least the number of elements which exist in the list at + * and after its current position. This array will be in the same order + * as the underlying list (not reversed). + *

+ * NOTE: This array may contain null members if any of the + * individual file listings failed to parse. The caller should + * check each entry for null before referencing it. + */ + public FTPFile[] getPrevious(int quantityRequested) { + List tmpResults = new LinkedList(); + int count = quantityRequested; + while (count > 0 && this._internalIterator.hasPrevious()) { + String entry = this._internalIterator.previous(); + FTPFile temp = this.parser.parseFTPEntry(entry); + if (temp == null && saveUnparseableEntries) { + temp = new FTPFile(entry); + } + tmpResults.add(0, temp); + count--; + } + return tmpResults.toArray(new FTPFile[tmpResults.size()]); + } + + /** + * Returns an array of FTPFile objects containing the whole list of + * files returned by the server as read by this object's parser. + * + * @return an array of FTPFile objects containing the whole list of + * files returned by the server as read by this object's parser. + * None of the entries will be null + * @throws IOException - not ever thrown, may be removed in a later release + */ + public FTPFile[] getFiles() throws IOException // TODO remove; not actually thrown + { + return getFiles(FTPFileFilters.NON_NULL); + } + + /** + * Returns an array of FTPFile objects containing the whole list of + * files returned by the server as read by this object's parser. + * The files are filtered before being added to the array. + * + * @param filter FTPFileFilter, must not be null. + * @return an array of FTPFile objects containing the whole list of + * files returned by the server as read by this object's parser. + *

+ * NOTE: This array may contain null members if any of the + * individual file listings failed to parse. The caller should + * check each entry for null before referencing it, or use the + * a filter such as {@link FTPFileFilters#NON_NULL} which does not + * allow null entries. + * @throws IOException - not ever thrown, may be removed in a later release + * @since 2.2 + */ + public FTPFile[] getFiles(FTPFileFilter filter) + throws IOException // TODO remove; not actually thrown + { + List tmpResults = new ArrayList(); + Iterator iter = this.entries.iterator(); + while (iter.hasNext()) { + String entry = iter.next(); + FTPFile temp = this.parser.parseFTPEntry(entry); + if (temp == null && saveUnparseableEntries) { + temp = new FTPFile(entry); + } + if (filter.accept(temp)) { + tmpResults.add(temp); + } + } + return tmpResults.toArray(new FTPFile[tmpResults.size()]); + } + + /** + * convenience method to allow clients to know whether this object's + * internal iterator's current position is at the end of the list. + * + * @return true if internal iterator is not at end of list, false + * otherwise. + */ + public boolean hasNext() { + return _internalIterator.hasNext(); + } + + /** + * convenience method to allow clients to know whether this object's + * internal iterator's current position is at the beginning of the list. + * + * @return true if internal iterator is not at beginning of list, false + * otherwise. + */ + public boolean hasPrevious() { + return _internalIterator.hasPrevious(); + } + + /** + * resets this object's internal iterator to the beginning of the list. + */ + public void resetIterator() { + this._internalIterator = this.entries.listIterator(); + } + + // DEPRECATED METHODS - for API compatibility only - DO NOT USE + + /** + * Do not use. + * + * @param stream the stream from which to read + * @throws IOException on error + * @deprecated use {@link #readServerList(InputStream, String)} instead + */ + @Deprecated public void readServerList(InputStream stream) throws IOException { + readServerList(stream, null); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPReply.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPReply.java new file mode 100644 index 00000000..991cb93d --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPReply.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +/*** + * FTPReply stores a set of constants for FTP reply codes. To interpret + * the meaning of the codes, familiarity with RFC 959 is assumed. + * The mnemonic constant names are transcriptions from the code descriptions + * of RFC 959. + *

+ * TODO replace with an enum + ***/ + +public final class FTPReply { + + public static final int RESTART_MARKER = 110; + public static final int SERVICE_NOT_READY = 120; + public static final int DATA_CONNECTION_ALREADY_OPEN = 125; + public static final int FILE_STATUS_OK = 150; + public static final int COMMAND_OK = 200; + public static final int COMMAND_IS_SUPERFLUOUS = 202; + public static final int SYSTEM_STATUS = 211; + public static final int DIRECTORY_STATUS = 212; + public static final int FILE_STATUS = 213; + public static final int HELP_MESSAGE = 214; + public static final int NAME_SYSTEM_TYPE = 215; + public static final int SERVICE_READY = 220; + public static final int SERVICE_CLOSING_CONTROL_CONNECTION = 221; + public static final int DATA_CONNECTION_OPEN = 225; + public static final int CLOSING_DATA_CONNECTION = 226; + public static final int ENTERING_PASSIVE_MODE = 227; + /** @since 2.2 */ + public static final int ENTERING_EPSV_MODE = 229; + public static final int USER_LOGGED_IN = 230; + public static final int FILE_ACTION_OK = 250; + public static final int PATHNAME_CREATED = 257; + public static final int NEED_PASSWORD = 331; + public static final int NEED_ACCOUNT = 332; + public static final int FILE_ACTION_PENDING = 350; + public static final int SERVICE_NOT_AVAILABLE = 421; + public static final int CANNOT_OPEN_DATA_CONNECTION = 425; + public static final int TRANSFER_ABORTED = 426; + public static final int FILE_ACTION_NOT_TAKEN = 450; + public static final int ACTION_ABORTED = 451; + public static final int INSUFFICIENT_STORAGE = 452; + public static final int UNRECOGNIZED_COMMAND = 500; + public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; + public static final int COMMAND_NOT_IMPLEMENTED = 502; + public static final int BAD_COMMAND_SEQUENCE = 503; + public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; + public static final int NOT_LOGGED_IN = 530; + public static final int NEED_ACCOUNT_FOR_STORING_FILES = 532; + public static final int FILE_UNAVAILABLE = 550; + public static final int PAGE_TYPE_UNKNOWN = 551; + public static final int STORAGE_ALLOCATION_EXCEEDED = 552; + public static final int FILE_NAME_NOT_ALLOWED = 553; + + // FTPS Reply Codes + + /** @since 2.0 */ + public static final int SECURITY_DATA_EXCHANGE_COMPLETE = 234; + /** @since 2.0 */ + public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = 235; + /** @since 2.0 */ + public static final int SECURITY_MECHANISM_IS_OK = 334; + /** @since 2.0 */ + public static final int SECURITY_DATA_IS_ACCEPTABLE = 335; + /** @since 2.0 */ + public static final int UNAVAILABLE_RESOURCE = 431; + /** @since 2.2 */ + public static final int BAD_TLS_NEGOTIATION_OR_DATA_ENCRYPTION_REQUIRED = 522; + /** @since 2.0 */ + public static final int DENIED_FOR_POLICY_REASONS = 533; + /** @since 2.0 */ + public static final int REQUEST_DENIED = 534; + /** @since 2.0 */ + public static final int FAILED_SECURITY_CHECK = 535; + /** @since 2.0 */ + public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = 536; + + // IPv6 error codes + // Note this is also used as an FTPS error code reply + /** @since 2.2 */ + public static final int EXTENDED_PORT_FAILURE = 522; + + // Cannot be instantiated + private FTPReply() { + } + + /*** + * Determine if a reply code is a positive preliminary response. All + * codes beginning with a 1 are positive preliminary responses. + * Postitive preliminary responses are used to indicate tentative success. + * No further commands can be issued to the FTP server after a positive + * preliminary response until a follow up response is received from the + * server. + * + * @param reply The reply code to test. + * @return True if a reply code is a postive preliminary response, false + * if not. + ***/ + public static boolean isPositivePreliminary(int reply) { + return (reply >= 100 && reply < 200); + } + + /*** + * Determine if a reply code is a positive completion response. All + * codes beginning with a 2 are positive completion responses. + * The FTP server will send a positive completion response on the final + * successful completion of a command. + * + * @param reply The reply code to test. + * @return True if a reply code is a postive completion response, false + * if not. + ***/ + public static boolean isPositiveCompletion(int reply) { + return (reply >= 200 && reply < 300); + } + + /*** + * Determine if a reply code is a positive intermediate response. All + * codes beginning with a 3 are positive intermediate responses. + * The FTP server will send a positive intermediate response on the + * successful completion of one part of a multi-part sequence of + * commands. For example, after a successful USER command, a positive + * intermediate response will be sent to indicate that the server is + * ready for the PASS command. + * + * @param reply The reply code to test. + * @return True if a reply code is a postive intermediate response, false + * if not. + ***/ + public static boolean isPositiveIntermediate(int reply) { + return (reply >= 300 && reply < 400); + } + + /*** + * Determine if a reply code is a negative transient response. All + * codes beginning with a 4 are negative transient responses. + * The FTP server will send a negative transient response on the + * failure of a command that can be reattempted with success. + * + * @param reply The reply code to test. + * @return True if a reply code is a negative transient response, false + * if not. + ***/ + public static boolean isNegativeTransient(int reply) { + return (reply >= 400 && reply < 500); + } + + /*** + * Determine if a reply code is a negative permanent response. All + * codes beginning with a 5 are negative permanent responses. + * The FTP server will send a negative permanent response on the + * failure of a command that cannot be reattempted with success. + * + * @param reply The reply code to test. + * @return True if a reply code is a negative permanent response, false + * if not. + ***/ + public static boolean isNegativePermanent(int reply) { + return (reply >= 500 && reply < 600); + } + + /** + * Determine if a reply code is a protected response. + * + * @param reply The reply code to test. + * @return True if a reply code is a protected response, false + * if not. + * @since 3.0 + */ + public static boolean isProtectedReplyCode(int reply) { + // actually, only 3 protected reply codes are + // defined in RFC 2228: 631, 632 and 633. + return (reply >= 600 && reply < 700); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java new file mode 100644 index 00000000..7f79e98b --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java @@ -0,0 +1,931 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import aria.apache.commons.net.SocketClient; +import aria.apache.commons.net.util.KeyManagerUtils; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import aria.apache.commons.net.util.Base64; +import aria.apache.commons.net.util.SSLContextUtils; +import aria.apache.commons.net.util.SSLSocketUtils; +import aria.apache.commons.net.util.TrustManagerUtils; + +/** + * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to + * see wire-level SSL details. + * + * Warning: the hostname is not verified against the certificate by default, use + * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)} + * (on Java 1.7+) to enable verification. Verification is only performed on client mode connections. + * + * @version $Id: FTPSClient.java 1747829 2016-06-11 00:57:57Z sebb $ + * @since 2.0 + */ +public class FTPSClient extends FTPClient { + + // From http://www.iana.org/assignments/port-numbers + + // ftps-data 989/tcp ftp protocol, data, over TLS/SSL + // ftps-data 989/udp ftp protocol, data, over TLS/SSL + // ftps 990/tcp ftp protocol, control, over TLS/SSL + // ftps 990/udp ftp protocol, control, over TLS/SSL + + public static final int DEFAULT_FTPS_DATA_PORT = 989; + public static final int DEFAULT_FTPS_PORT = 990; + + /** The value that I can set in PROT command (C = Clear, P = Protected) */ + private static final String[] PROT_COMMAND_VALUE = { "C", "E", "S", "P" }; + /** Default PROT Command */ + private static final String DEFAULT_PROT = "C"; + /** Default secure socket protocol name, i.e. TLS */ + private static final String DEFAULT_PROTOCOL = "TLS"; + + /** The AUTH (Authentication/Security Mechanism) command. */ + private static final String CMD_AUTH = "AUTH"; + /** The ADAT (Authentication/Security Data) command. */ + private static final String CMD_ADAT = "ADAT"; + /** The PROT (Data Channel Protection Level) command. */ + private static final String CMD_PROT = "PROT"; + /** The PBSZ (Protection Buffer Size) command. */ + private static final String CMD_PBSZ = "PBSZ"; + /** The MIC (Integrity Protected Command) command. */ + private static final String CMD_MIC = "MIC"; + /** The CONF (Confidentiality Protected Command) command. */ + private static final String CMD_CONF = "CONF"; + /** The ENC (Privacy Protected Command) command. */ + private static final String CMD_ENC = "ENC"; + /** The CCC (Clear Command Channel) command. */ + private static final String CMD_CCC = "CCC"; + + /** The security mode. (True - Implicit Mode / False - Explicit Mode) */ + private final boolean isImplicit; + /** The secure socket protocol to be used, e.g. SSL/TLS. */ + private final String protocol; + /** The AUTH Command value */ + private String auth = DEFAULT_PROTOCOL; + /** The context object. */ + private SSLContext context; + /** The socket object. */ + private Socket plainSocket; + /** Controls whether a new SSL session may be established by this socket. Default true. */ + private boolean isCreation = true; + /** The use client mode flag. */ + private boolean isClientMode = true; + /** The need client auth flag. */ + private boolean isNeedClientAuth = false; + /** The want client auth flag. */ + private boolean isWantClientAuth = false; + /** The cipher suites */ + private String[] suites = null; + /** The protocol versions */ + private String[] protocols = null; + + /** + * The FTPS {@link TrustManager} implementation, default validate only + * {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. + */ + private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager(); + + /** The {@link KeyManager}, default null (i.e. use system default). */ + private KeyManager keyManager = null; + + /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ + private HostnameVerifier hostnameVerifier = null; + + /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ + private boolean tlsEndpointChecking; + + /** + * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}. + * + * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit + * (isImplicit = false) + */ + public FTPSClient() { + this(DEFAULT_PROTOCOL, false); + } + + /** + * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS + * Calls {@link #FTPSClient(String, boolean)} + * + * @param isImplicit The security mode (Implicit/Explicit). + */ + public FTPSClient(boolean isImplicit) { + this(DEFAULT_PROTOCOL, isImplicit); + } + + /** + * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}. + * + * @param protocol the protocol to use + */ + public FTPSClient(String protocol) { + this(protocol, false); + } + + /** + * Constructor for FTPSClient allowing specification of protocol + * and security mode. If isImplicit is true, the port is set to + * {@link #DEFAULT_FTPS_PORT} i.e. 990. + * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} + * + * @param protocol the protocol + * @param isImplicit The security mode(Implicit/Explicit). + */ + public FTPSClient(String protocol, boolean isImplicit) { + super(); + this.protocol = protocol; + this.isImplicit = isImplicit; + if (isImplicit) { + setDefaultPort(DEFAULT_FTPS_PORT); + } + } + + /** + * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS + * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} + * + * @param isImplicit The security mode(Implicit/Explicit). + * @param context A pre-configured SSL Context + */ + public FTPSClient(boolean isImplicit, SSLContext context) { + this(DEFAULT_PROTOCOL, isImplicit); + this.context = context; + } + + /** + * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS + * and isImplicit {@code false} + * Calls {@link #FTPSClient(boolean, SSLContext)} + * + * @param context A pre-configured SSL Context + */ + public FTPSClient(SSLContext context) { + this(false, context); + } + + /** + * Set AUTH command use value. + * This processing is done before connected processing. + * + * @param auth AUTH command use value. + */ + public void setAuthValue(String auth) { + this.auth = auth; + } + + /** + * Return AUTH command use value. + * + * @return AUTH command use value. + */ + public String getAuthValue() { + return this.auth; + } + + /** + * Because there are so many connect() methods, + * the _connectAction_() method is provided as a means of performing + * some action immediately after establishing a connection, + * rather than reimplementing all of the connect() methods. + * + * @throws IOException If it throw by _connectAction_. + * @see SocketClient#_connectAction_() + */ + @Override protected void _connectAction_() throws IOException { + // Implicit mode. + if (isImplicit) { + sslNegotiation(); + } + super._connectAction_(); + // Explicit mode. + if (!isImplicit) { + execAUTH(); + sslNegotiation(); + } + } + + /** + * AUTH command. + * + * @throws SSLException If it server reply code not equal "234" and "334". + * @throws IOException If an I/O error occurs while either sending + * the command. + */ + protected void execAUTH() throws SSLException, IOException { + int replyCode = sendCommand(CMD_AUTH, auth); + if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) { + // replyCode = 334 + // I carry out an ADAT command. + } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) { + throw new SSLException(getReplyString()); + } + } + + /** + * Performs a lazy init of the SSL context + * + * @throws IOException + */ + private void initSslContext() throws IOException { + if (context == null) { + context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); + } + } + + /** + * SSL/TLS negotiation. Acquires an SSL socket of a control + * connection and carries out handshake processing. + * + * @throws IOException If server negotiation fails + */ + protected void sslNegotiation() throws IOException { + plainSocket = _socket_; + initSslContext(); + + SSLSocketFactory ssf = context.getSocketFactory(); + String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress(); + int port = _socket_.getPort(); + SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, false); + socket.setEnableSessionCreation(isCreation); + socket.setUseClientMode(isClientMode); + + // client mode + if (isClientMode) { + if (tlsEndpointChecking) { + SSLSocketUtils.enableEndpointNameVerification(socket); + } + } else { // server mode + socket.setNeedClientAuth(isNeedClientAuth); + socket.setWantClientAuth(isWantClientAuth); + } + + if (protocols != null) { + socket.setEnabledProtocols(protocols); + } + if (suites != null) { + socket.setEnabledCipherSuites(suites); + } + socket.startHandshake(); + + // TODO the following setup appears to duplicate that in the super class methods + _socket_ = socket; + _controlInput_ = + new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); + _controlOutput_ = + new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), getControlEncoding())); + + if (isClientMode) { + if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { + throw new SSLHandshakeException("Hostname doesn't match certificate"); + } + } + } + + /** + * Get the {@link KeyManager} instance. + * + * @return The {@link KeyManager} instance + */ + private KeyManager getKeyManager() { + return keyManager; + } + + /** + * Set a {@link KeyManager} to use + * + * @param keyManager The KeyManager implementation to set. + * @see KeyManagerUtils + */ + public void setKeyManager(KeyManager keyManager) { + this.keyManager = keyManager; + } + + /** + * Controls whether a new SSL session may be established by this socket. + * + * @param isCreation The established socket flag. + */ + public void setEnabledSessionCreation(boolean isCreation) { + this.isCreation = isCreation; + } + + /** + * Returns true if new SSL sessions may be established by this socket. + * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an + * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) + * enabled, + * this returns False. + * + * @return true - Indicates that sessions may be created; + * this is the default. + * false - indicates that an existing session must be resumed. + */ + public boolean getEnableSessionCreation() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getEnableSessionCreation(); + } + return false; + } + + /** + * Configures the socket to require client authentication. + * + * @param isNeedClientAuth The need client auth flag. + */ + public void setNeedClientAuth(boolean isNeedClientAuth) { + this.isNeedClientAuth = isNeedClientAuth; + } + + /** + * Returns true if the socket will require client authentication. + * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. + * + * @return true - If the server mode socket should request + * that the client authenticate itself. + */ + public boolean getNeedClientAuth() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getNeedClientAuth(); + } + return false; + } + + /** + * Configures the socket to request client authentication, + * but only if such a request is appropriate to the cipher + * suite negotiated. + * + * @param isWantClientAuth The want client auth flag. + */ + public void setWantClientAuth(boolean isWantClientAuth) { + this.isWantClientAuth = isWantClientAuth; + } + + /** + * Returns true if the socket will request client authentication. + * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. + * + * @return true - If the server mode socket should request + * that the client authenticate itself. + */ + public boolean getWantClientAuth() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getWantClientAuth(); + } + return false; + } + + /** + * Configures the socket to use client (or server) mode in its first + * handshake. + * + * @param isClientMode The use client mode flag. + */ + public void setUseClientMode(boolean isClientMode) { + this.isClientMode = isClientMode; + } + + /** + * Returns true if the socket is set to use client mode + * in its first handshake. + * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. + * + * @return true - If the socket should start its first handshake + * in "client" mode. + */ + public boolean getUseClientMode() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getUseClientMode(); + } + return false; + } + + /** + * Controls which particular cipher suites are enabled for use on this + * connection. Called before server negotiation. + * + * @param cipherSuites The cipher suites. + */ + public void setEnabledCipherSuites(String[] cipherSuites) { + suites = new String[cipherSuites.length]; + System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); + } + + /** + * Returns the names of the cipher suites which could be enabled + * for use on this connection. + * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. + * + * @return An array of cipher suite names, or null + */ + public String[] getEnabledCipherSuites() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getEnabledCipherSuites(); + } + return null; + } + + /** + * Controls which particular protocol versions are enabled for use on this + * connection. I perform setting before a server negotiation. + * + * @param protocolVersions The protocol versions. + */ + public void setEnabledProtocols(String[] protocolVersions) { + protocols = new String[protocolVersions.length]; + System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); + } + + /** + * Returns the names of the protocol versions which are currently + * enabled for use on this connection. + * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. + * + * @return An array of protocols, or null + */ + public String[] getEnabledProtocols() { + if (_socket_ instanceof SSLSocket) { + return ((SSLSocket) _socket_).getEnabledProtocols(); + } + return null; + } + + /** + * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. + * + * @param pbsz Protection Buffer Size. + * @throws SSLException If the server reply code does not equal "200". + * @throws IOException If an I/O error occurs while sending + * the command. + * @see #parsePBSZ(long) + */ + public void execPBSZ(long pbsz) throws SSLException, IOException { + if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number + throw new IllegalArgumentException(); + } + int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz)); + if (FTPReply.COMMAND_OK != status) { + throw new SSLException(getReplyString()); + } + } + + /** + * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. + * Issues the command and parses the response to return the negotiated value. + * + * @param pbsz Protection Buffer Size. + * @return the negotiated value. + * @throws SSLException If the server reply code does not equal "200". + * @throws IOException If an I/O error occurs while sending + * the command. + * @see #execPBSZ(long) + * @since 3.0 + */ + public long parsePBSZ(long pbsz) throws SSLException, IOException { + execPBSZ(pbsz); + long minvalue = pbsz; + String remainder = extractPrefixedData("PBSZ=", getReplyString()); + if (remainder != null) { + long replysz = Long.parseLong(remainder); + if (replysz < minvalue) { + minvalue = replysz; + } + } + return minvalue; + } + + /** + * PROT command. + *

    + *
  • C - Clear
  • + *
  • S - Safe(SSL protocol only)
  • + *
  • E - Confidential(SSL protocol only)
  • + *
  • P - Private
  • + *
+ * N.B. the method calls + * {@link #setSocketFactory(javax.net.SocketFactory)} and + * {@link #setServerSocketFactory(javax.net.ServerSocketFactory)} + * + * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. + * @throws SSLException If the server reply code does not equal {@code 200}. + * @throws IOException If an I/O error occurs while sending + * the command. + */ + public void execPROT(String prot) throws SSLException, IOException { + if (prot == null) { + prot = DEFAULT_PROT; + } + if (!checkPROTValue(prot)) { + throw new IllegalArgumentException(); + } + if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) { + throw new SSLException(getReplyString()); + } + if (DEFAULT_PROT.equals(prot)) { + setSocketFactory(null); + setServerSocketFactory(null); + } else { + setSocketFactory(new FTPSSocketFactory(context)); + setServerSocketFactory(new FTPSServerSocketFactory(context)); + initSslContext(); + } + } + + /** + * Check the value that can be set in PROT Command value. + * + * @param prot Data Channel Protection Level. + * @return True - A set point is right / False - A set point is not right + */ + private boolean checkPROTValue(String prot) { + for (String element : PROT_COMMAND_VALUE) { + if (element.equals(prot)) { + return true; + } + } + return false; + } + + /** + * Send an FTP command. + * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} + * instance to be assigned to a plain {@link Socket} + * + * @param command The FTP command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending the command. + * @throws SSLException if a CCC command fails + * @see FTP#sendCommand(String) + */ + // Would like to remove this method, but that will break any existing clients that are using CCC + @Override public int sendCommand(String command, String args) throws IOException { + int repCode = super.sendCommand(command, args); + /* If CCC is issued, restore socket i/o streams to unsecured versions */ + if (CMD_CCC.equals(command)) { + if (FTPReply.COMMAND_OK == repCode) { + _socket_.close(); + _socket_ = plainSocket; + _controlInput_ = new BufferedReader( + new InputStreamReader(_socket_.getInputStream(), getControlEncoding())); + _controlOutput_ = new BufferedWriter( + new OutputStreamWriter(_socket_.getOutputStream(), getControlEncoding())); + } else { + throw new SSLException(getReplyString()); + } + } + return repCode; + } + + /** + * Returns a socket of the data connection. + * Wrapped as an {@link SSLSocket}, which carries out handshake processing. + * + * @param command The int representation of the FTP command to send. + * @param arg The arguments to the FTP command. + * If this parameter is set to null, then the command is sent with + * no arguments. + * @return corresponding to the established data connection. + * Null is returned if an FTP protocol error is reported at any point + * during the establishment and initialization of the connection. + * @throws IOException If there is any problem with the connection. + * @see FTPClient#_openDataConnection_(int, String) + * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead + */ + @Override + // Strictly speaking this is not needed, but it works round a Clirr bug + // So rather than invoke the parent code, we do it here + @Deprecated protected Socket _openDataConnection_(int command, String arg) throws IOException { + return _openDataConnection_(FTPCommand.getCommand(command), arg); + } + + /** + * Returns a socket of the data connection. + * Wrapped as an {@link SSLSocket}, which carries out handshake processing. + * + * @param command The textual representation of the FTP command to send. + * @param arg The arguments to the FTP command. + * If this parameter is set to null, then the command is sent with + * no arguments. + * @return corresponding to the established data connection. + * Null is returned if an FTP protocol error is reported at any point + * during the establishment and initialization of the connection. + * @throws IOException If there is any problem with the connection. + * @see FTPClient#_openDataConnection_(int, String) + * @since 3.2 + */ + @Override protected Socket _openDataConnection_(String command, String arg) throws IOException { + Socket socket = super._openDataConnection_(command, arg); + _prepareDataSocket_(socket); + if (socket instanceof SSLSocket) { + SSLSocket sslSocket = (SSLSocket) socket; + + sslSocket.setUseClientMode(isClientMode); + sslSocket.setEnableSessionCreation(isCreation); + + // server mode + if (!isClientMode) { + sslSocket.setNeedClientAuth(isNeedClientAuth); + sslSocket.setWantClientAuth(isWantClientAuth); + } + if (suites != null) { + sslSocket.setEnabledCipherSuites(suites); + } + if (protocols != null) { + sslSocket.setEnabledProtocols(protocols); + } + sslSocket.startHandshake(); + } + + return socket; + } + + /** + * Performs any custom initialization for a newly created SSLSocket (before + * the SSL handshake happens). + * Called by {@link #_openDataConnection_(int, String)} immediately + * after creating the socket. + * The default implementation is a no-op + * + * @param socket the socket to set up + * @throws IOException on error + * @since 3.1 + */ + protected void _prepareDataSocket_(Socket socket) throws IOException { + } + + /** + * Get the currently configured {@link TrustManager}. + * + * @return A TrustManager instance. + */ + public TrustManager getTrustManager() { + return trustManager; + } + + /** + * Override the default {@link TrustManager} to use; if set to {@code null}, + * the default TrustManager from the JVM will be used. + * + * @param trustManager The TrustManager implementation to set, may be {@code null} + * @see TrustManagerUtils + */ + public void setTrustManager(TrustManager trustManager) { + this.trustManager = trustManager; + } + + /** + * Get the currently configured {@link HostnameVerifier}. + * The verifier is only used on client mode connections. + * + * @return A HostnameVerifier instance. + * @since 3.4 + */ + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + /** + * Override the default {@link HostnameVerifier} to use. + * The verifier is only used on client mode connections. + * + * @param newHostnameVerifier The HostnameVerifier implementation to set or null to + * disable. + * @since 3.4 + */ + public void setHostnameVerifier(HostnameVerifier newHostnameVerifier) { + hostnameVerifier = newHostnameVerifier; + } + + /** + * Return whether or not endpoint identification using the HTTPS algorithm + * on Java 1.7+ is enabled. The default behaviour is for this to be disabled. + * + * This check is only performed on client mode connections. + * + * @return True if enabled, false if not. + * @since 3.4 + */ + public boolean isEndpointCheckingEnabled() { + return tlsEndpointChecking; + } + + /** + * Automatic endpoint identification checking using the HTTPS algorithm + * is supported on Java 1.7+. The default behaviour is for this to be disabled. + * + * This check is only performed on client mode connections. + * + * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on + * Java 1.7+. + * @since 3.4 + */ + public void setEndpointCheckingEnabled(boolean enable) { + tlsEndpointChecking = enable; + } + + /** + * Closes the connection to the FTP server and restores + * connection parameters to the default values. + *

+ * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)} + * to reset the factories that may have been changed during the session, + * e.g. by {@link #execPROT(String)} + * + * @throws IOException If an error occurs while disconnecting. + * @since 3.0 + */ + @Override public void disconnect() throws IOException { + super.disconnect(); + if (plainSocket != null) { + plainSocket.close(); + } + setSocketFactory(null); + setServerSocketFactory(null); + } + + /** + * Send the AUTH command with the specified mechanism. + * + * @param mechanism The mechanism name to send with the command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execAUTH(String mechanism) throws IOException { + return sendCommand(CMD_AUTH, mechanism); + } + + /** + * Send the ADAT command with the specified authentication data. + * + * @param data The data to send with the command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execADAT(byte[] data) throws IOException { + if (data != null) { + return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data)); + } else { + return sendCommand(CMD_ADAT); + } + } + + /** + * Send the CCC command to the server. + * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be + * assigned + * to a plain {@link Socket} instances + * + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execCCC() throws IOException { + int repCode = sendCommand(CMD_CCC); + // This will be performed by sendCommand(String, String) + // if (FTPReply.isPositiveCompletion(repCode)) { + // _socket_.close(); + // _socket_ = plainSocket; + // _controlInput_ = new BufferedReader( + // new InputStreamReader( + // _socket_.getInputStream(), getControlEncoding())); + // _controlOutput_ = new BufferedWriter( + // new OutputStreamWriter( + // _socket_.getOutputStream(), getControlEncoding())); + // } + return repCode; + } + + /** + * Send the MIC command with the specified data. + * + * @param data The data to send with the command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execMIC(byte[] data) throws IOException { + if (data != null) { + return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data)); + } else { + return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)? + } + } + + /** + * Send the CONF command with the specified data. + * + * @param data The data to send with the command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execCONF(byte[] data) throws IOException { + if (data != null) { + return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data)); + } else { + return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)? + } + } + + /** + * Send the ENC command with the specified data. + * + * @param data The data to send with the command. + * @return server reply. + * @throws IOException If an I/O error occurs while sending + * the command. + * @since 3.0 + */ + public int execENC(byte[] data) throws IOException { + if (data != null) { + return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data)); + } else { + return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)? + } + } + + /** + * Parses the given ADAT response line and base64-decodes the data. + * + * @param reply The ADAT reply to parse. + * @return the data in the reply, base64-decoded. + * @since 3.0 + */ + public byte[] parseADATReply(String reply) { + if (reply == null) { + return null; + } else { + return Base64.decodeBase64(extractPrefixedData("ADAT=", reply)); + } + } + + /** + * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234 + * + * @param prefix the prefix to find + * @param reply where to find the prefix + * @return the remainder of the string after the prefix, or null if the prefix was not present. + */ + private String extractPrefixedData(String prefix, String reply) { + int idx = reply.indexOf(prefix); + if (idx == -1) { + return null; + } + // N.B. Cannot use trim before substring as leading space would affect the offset. + return reply.substring(idx + prefix.length()).trim(); + } + + // DEPRECATED - for API compatibility only - DO NOT USE + + /** @deprecated - not used - may be removed in a future release */ + @Deprecated public static String KEYSTORE_ALGORITHM; + + /** @deprecated - not used - may be removed in a future release */ + @Deprecated public static String TRUSTSTORE_ALGORITHM; + + /** @deprecated - not used - may be removed in a future release */ + @Deprecated public static String PROVIDER; + + /** @deprecated - not used - may be removed in a future release */ + @Deprecated public static String STORE_TYPE; +} +/* kate: indent-width 4; replace-tabs on; */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java new file mode 100644 index 00000000..5937638b --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +/** + * FTPS-specific commands. + * + * @since 2.0 + * @deprecated 3.0 DO NOT USE + */ +@Deprecated public final class FTPSCommand { + public static final int AUTH = 0; + public static final int ADAT = 1; + public static final int PBSZ = 2; + public static final int PROT = 3; + public static final int CCC = 4; + + public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH; + public static final int AUTHENTICATION_SECURITY_DATA = ADAT; + public static final int PROTECTION_BUFFER_SIZE = PBSZ; + public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT; + public static final int CLEAR_COMMAND_CHANNEL = CCC; + + private static final String[] _commands = { "AUTH", "ADAT", "PBSZ", "PROT", "CCC" }; + + /** + * Retrieve the FTPS command string corresponding to a specified + * command code. + * + * @param command The command code. + * @return The FTPS command string corresponding to a specified + * command code. + */ + public static final String getCommand(int command) { + return _commands[command]; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java new file mode 100644 index 00000000..168067bc --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; + +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; + +/** + * Server socket factory for FTPS connections. + * + * @since 2.2 + */ +public class FTPSServerSocketFactory extends ServerSocketFactory { + + /** Factory for secure socket factories */ + private final SSLContext context; + + public FTPSServerSocketFactory(SSLContext context) { + this.context = context; + } + + // Override the default superclass method + @Override public ServerSocket createServerSocket() throws IOException { + return init(this.context.getServerSocketFactory().createServerSocket()); + } + + @Override public ServerSocket createServerSocket(int port) throws IOException { + return init(this.context.getServerSocketFactory().createServerSocket(port)); + } + + @Override public ServerSocket createServerSocket(int port, int backlog) throws IOException { + return init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); + } + + @Override public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) + throws IOException { + return init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); + } + + /** + * Sets the socket so newly accepted connections will use SSL client mode. + * + * @param socket the SSLServerSocket to initialise + * @return the socket + * @throws ClassCastException if socket is not an instance of SSLServerSocket + */ + public ServerSocket init(ServerSocket socket) { + ((SSLServerSocket) socket).setUseClientMode(true); + return socket; + } +} + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java new file mode 100644 index 00000000..7bbf818c --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; + +/** + * Implementation of org.apache.commons.net.SocketFactory + * + * @since 2.0 + */ +public class FTPSSocketFactory extends SocketFactory { + + private final SSLContext context; + + public FTPSSocketFactory(SSLContext context) { + this.context = context; + } + + // Override the default implementation + @Override public Socket createSocket() throws IOException { + return this.context.getSocketFactory().createSocket(); + } + + @Override public Socket createSocket(String address, int port) + throws UnknownHostException, IOException { + return this.context.getSocketFactory().createSocket(address, port); + } + + @Override public Socket createSocket(InetAddress address, int port) throws IOException { + return this.context.getSocketFactory().createSocket(address, port); + } + + @Override + public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) + throws UnknownHostException, IOException { + return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) + throws IOException { + return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); + } + + // DEPRECATED METHODS - for API compatibility only - DO NOT USE + + /** + * @param port the port + * @return the socket + * @throws IOException on error + * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int) instead} + */ + @Deprecated public java.net.ServerSocket createServerSocket(int port) throws IOException { + return this.init(this.context.getServerSocketFactory().createServerSocket(port)); + } + + /** + * @param port the port + * @param backlog the backlog + * @return the socket + * @throws IOException on error + * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int) instead} + */ + @Deprecated public java.net.ServerSocket createServerSocket(int port, int backlog) + throws IOException { + return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); + } + + /** + * @param port the port + * @param backlog the backlog + * @param ifAddress the interface + * @return the socket + * @throws IOException on error + * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int, InetAddress) + * instead} + */ + @Deprecated public java.net.ServerSocket createServerSocket(int port, int backlog, + InetAddress ifAddress) throws IOException { + return this.init( + this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); + } + + /** + * @param socket the socket + * @return the socket + * @throws IOException on error + * @deprecated (2.2) use {@link FTPSServerSocketFactory#init(java.net.ServerSocket)} + */ + @Deprecated public java.net.ServerSocket init(java.net.ServerSocket socket) throws IOException { + ((javax.net.ssl.SSLServerSocket) socket).setUseClientMode(true); + return socket; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java new file mode 100644 index 00000000..0923ac02 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp; + +import aria.apache.commons.net.util.TrustManagerUtils; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +/** + * Do not use. + * + * @since 2.0 + * @deprecated 3.0 use + * {@link TrustManagerUtils#getValidateServerCertificateTrustManager() + * TrustManagerUtils#getValidateServerCertificateTrustManager()} instead + */ +@Deprecated public class FTPSTrustManager implements X509TrustManager { + private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[] {}; + + /** + * No-op + */ + @Override public void checkClientTrusted(X509Certificate[] certificates, String authType) { + return; + } + + @Override public void checkServerTrusted(X509Certificate[] certificates, String authType) + throws CertificateException { + for (X509Certificate certificate : certificates) { + certificate.checkValidity(); + } + } + + @Override public X509Certificate[] getAcceptedIssuers() { + return EMPTY_X509CERTIFICATE_ARRAY; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java new file mode 100644 index 00000000..f7adaf6d --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aria.apache.commons.net.ftp; + +import aria.apache.commons.net.io.CopyStreamListener; + +/** + * Created by AriaL on 2017/9/26. + * ftp 上传文件流事件监听 + */ +public interface OnFtpInputStreamListener { + /** + * {@link CopyStreamListener#bytesTransferred(long, int, long)} + * + * @param totalBytesTransferred 已经上传的文件长度 + * @param bytesTransferred 上传byte长度 + */ + void onFtpInputStream(FTPClient client, long totalBytesTransferred, int bytesTransferred, + long streamSize); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/package-info.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/package-info.java new file mode 100644 index 00000000..f7df7cf0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * FTP and FTPS support classes + */ +package aria.apache.commons.net.ftp; \ No newline at end of file diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java new file mode 100644 index 00000000..638f67fb --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; + +/** + * This implementation allows to pack some FileEntryParsers together + * and handle the case where to returned dirstyle isnt clearly defined. + * The matching parser will be cached. + * If the cached parser wont match due to the server changed the dirstyle, + * a new matching parser will be searched. + */ +public class CompositeFileEntryParser extends FTPFileEntryParserImpl { + private final FTPFileEntryParser[] ftpFileEntryParsers; + private FTPFileEntryParser cachedFtpFileEntryParser; + + public CompositeFileEntryParser(FTPFileEntryParser[] ftpFileEntryParsers) { + super(); + + this.cachedFtpFileEntryParser = null; + this.ftpFileEntryParsers = ftpFileEntryParsers; + } + + @Override public FTPFile parseFTPEntry(String listEntry) { + if (cachedFtpFileEntryParser != null) { + FTPFile matched = cachedFtpFileEntryParser.parseFTPEntry(listEntry); + if (matched != null) { + return matched; + } + } else { + for (FTPFileEntryParser ftpFileEntryParser : ftpFileEntryParsers) { + FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry); + if (matched != null) { + cachedFtpFileEntryParser = ftpFileEntryParser; + return matched; + } + } + } + return null; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java new file mode 100644 index 00000000..1d8988b6 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.Configurable; +import aria.apache.commons.net.ftp.FTPClientConfig; +import java.text.ParseException; +import java.util.Calendar; + +/** + *

+ * This abstract class implements the common timestamp parsing + * algorithm for all the concrete parsers. Classes derived from + * this one will parse file listings via a supplied regular expression + * that pulls out the date portion as a separate string which is + * passed to the underlying {@link FTPTimestampParser delegate} to + * handle parsing of the file timestamp. + *

+ * This class also implements the {@link Configurable Configurable} + * interface to allow the parser to be configured from the outside. + * + * @since 1.4 + */ +public abstract class ConfigurableFTPFileEntryParserImpl extends RegexFTPFileEntryParserImpl + implements Configurable { + + private final FTPTimestampParser timestampParser; + + /** + * constructor for this abstract class. + * + * @param regex Regular expression used main parsing of the + * file listing. + */ + public ConfigurableFTPFileEntryParserImpl(String regex) { + super(regex); + this.timestampParser = new FTPTimestampParserImpl(); + } + + /** + * constructor for this abstract class. + * + * @param regex Regular expression used main parsing of the + * file listing. + * @param flags the flags to apply, see + * {@link java.util.regex.Pattern#compile(String, int) Pattern#compile(String, int)}. Use 0 for + * none. + * @since 3.4 + */ + public ConfigurableFTPFileEntryParserImpl(String regex, int flags) { + super(regex, flags); + this.timestampParser = new FTPTimestampParserImpl(); + } + + /** + * This method is called by the concrete parsers to delegate + * timestamp parsing to the timestamp parser. + * + * @param timestampStr the timestamp string pulled from the + * file listing by the regular expression parser, to be submitted + * to the timestampParser for extracting the timestamp. + * @return a java.util.Calendar containing results of the + * timestamp parse. + * @throws ParseException on parse error + */ + public Calendar parseTimestamp(String timestampStr) throws ParseException { + return this.timestampParser.parseTimestamp(timestampStr); + } + + /** + * Implementation of the {@link Configurable Configurable} + * interface. Configures this parser by delegating to the + * underlying Configurable FTPTimestampParser implementation, ' + * passing it the supplied {@link FTPClientConfig FTPClientConfig} + * if that is non-null or a default configuration defined by + * each concrete subclass. + * + * @param config the configuration to be used to configure this parser. + * If it is null, a default configuration defined by + * each concrete subclass is used instead. + */ + @Override public void configure(FTPClientConfig config) { + if (this.timestampParser instanceof Configurable) { + FTPClientConfig defaultCfg = getDefaultConfiguration(); + if (config != null) { + if (null == config.getDefaultDateFormatStr()) { + config.setDefaultDateFormatStr(defaultCfg.getDefaultDateFormatStr()); + } + if (null == config.getRecentDateFormatStr()) { + config.setRecentDateFormatStr(defaultCfg.getRecentDateFormatStr()); + } + ((Configurable) this.timestampParser).configure(config); + } else { + ((Configurable) this.timestampParser).configure(defaultCfg); + } + } + } + + /** + * Each concrete subclass must define this member to create + * a default configuration to be used when that subclass is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for the subclass. + */ + protected abstract FTPClientConfig getDefaultConfiguration(); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java new file mode 100644 index 00000000..cc499c71 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.Configurable; +import aria.apache.commons.net.ftp.FTPClient; +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.util.regex.Pattern; + +/** + * This is the default implementation of the + * FTPFileEntryParserFactory interface. This is the + * implementation that will be used by + * FTPClient.listFiles() + * if no other implementation has been specified. + * + * @see FTPClient#listFiles + * @see FTPClient#setParserFactory + */ +public class DefaultFTPFileEntryParserFactory implements FTPFileEntryParserFactory { + + // Match a plain Java Identifier + private static final String JAVA_IDENTIFIER = + "\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart})*"; + // Match a qualified name, e.g. a.b.c.Name - but don't allow the default package as that would allow "VMS"/"UNIX" etc. + private static final String JAVA_QUALIFIED_NAME = + "(" + JAVA_IDENTIFIER + "\\.)+" + JAVA_IDENTIFIER; + // Create the pattern, as it will be reused many times + private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME); + + /** + * This default implementation of the FTPFileEntryParserFactory + * interface works according to the following logic: + * First it attempts to interpret the supplied key as a fully + * qualified classname (default package is not allowed) of a class implementing the + * FTPFileEntryParser interface. If that succeeds, a parser + * object of this class is instantiated and is returned; + * otherwise it attempts to interpret the key as an identirier + * commonly used by the FTP SYST command to identify systems. + *

+ * If key is not recognized as a fully qualified + * classname known to the system, this method will then attempt + * to see whether it contains a string identifying one of + * the known parsers. This comparison is case-insensitive. + * The intent here is where possible, to select as keys strings + * which are returned by the SYST command on the systems which + * the corresponding parser successfully parses. This enables + * this factory to be used in the auto-detection system. + * + * @param key should be a fully qualified classname corresponding to + * a class implementing the FTPFileEntryParser interface
+ * OR
+ * a string containing (case-insensitively) one of the + * following keywords: + *

    + *
  • {@link FTPClientConfig#SYST_UNIX UNIX}
  • + *
  • {@link FTPClientConfig#SYST_NT WINDOWS}
  • + *
  • {@link FTPClientConfig#SYST_OS2 OS/2}
  • + *
  • {@link FTPClientConfig#SYST_OS400 OS/400}
  • + *
  • {@link FTPClientConfig#SYST_AS400 AS/400}
  • + *
  • {@link FTPClientConfig#SYST_VMS VMS}
  • + *
  • {@link FTPClientConfig#SYST_MVS MVS}
  • + *
  • {@link FTPClientConfig#SYST_NETWARE NETWARE}
  • + *
  • {@link FTPClientConfig#SYST_L8 TYPE:L8}
  • + *
+ * @return the FTPFileEntryParser corresponding to the supplied key. + * @throws ParserInitializationException thrown if for any reason the factory cannot resolve + * the supplied key into an FTPFileEntryParser. + * @see FTPFileEntryParser + */ + @Override public FTPFileEntryParser createFileEntryParser(String key) { + if (key == null) { + throw new ParserInitializationException("Parser key cannot be null"); + } + return createFileEntryParser(key, null); + } + + // Common method to process both key and config parameters. + private FTPFileEntryParser createFileEntryParser(String key, FTPClientConfig config) { + FTPFileEntryParser parser = null; + + // Is the key a possible class name? + if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { + try { + Class parserClass = Class.forName(key); + try { + parser = (FTPFileEntryParser) parserClass.newInstance(); + } catch (ClassCastException e) { + throw new ParserInitializationException(parserClass.getName() + + " does not implement the interface " + + "FTPFileEntryParser.", e); + } catch (Exception e) { + throw new ParserInitializationException("Error initializing parser", e); + } catch (ExceptionInInitializerError e) { + throw new ParserInitializationException("Error initializing parser", e); + } + } catch (ClassNotFoundException e) { + // OK, assume it is an alias + } + } + + if (parser == null) { // Now try for aliases + String ukey = key.toUpperCase(java.util.Locale.ENGLISH); + if (ukey.indexOf(FTPClientConfig.SYST_UNIX_TRIM_LEADING) >= 0) { + parser = new UnixFTPEntryParser(config, true); + } + // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it + else if (ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0) { + parser = new UnixFTPEntryParser(config, false); + } else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0) { + parser = new VMSVersioningFTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0) { + parser = createNTFTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0) { + parser = new OS2FTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 + || ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0) { + parser = createOS400FTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0) { + parser = new MVSFTPEntryParser(); // Does not currently support config parameter + } else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0) { + parser = new NetwareFTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_MACOS_PETER) >= 0) { + parser = new MacOsPeterFTPEntryParser(config); + } else if (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0) { + // L8 normally means Unix, but move it to the end for some L8 systems that aren't. + // This check should be last! + parser = new UnixFTPEntryParser(config); + } else { + throw new ParserInitializationException("Unknown parser type: " + key); + } + } + + if (parser instanceof Configurable) { + ((Configurable) parser).configure(config); + } + return parser; + } + + /** + *

Implementation extracts a key from the supplied + * {@link FTPClientConfig FTPClientConfig} + * parameter and creates an object implementing the + * interface FTPFileEntryParser and uses the supplied configuration + * to configure it. + *

+ * Note that this method will generally not be called in scenarios + * that call for autodetection of parser type but rather, for situations + * where the user knows that the server uses a non-default configuration + * and knows what that configuration is. + *

+ * + * @param config A {@link FTPClientConfig FTPClientConfig} + * used to configure the parser created + * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. + * @throws ParserInitializationException Thrown on any exception in instantiation + * @throws NullPointerException if {@code config} is {@code null} + * @since 1.4 + */ + @Override public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) + throws ParserInitializationException { + String key = config.getServerSystemKey(); + return createFileEntryParser(key, config); + } + + public FTPFileEntryParser createUnixFTPEntryParser() { + return new UnixFTPEntryParser(); + } + + public FTPFileEntryParser createVMSVersioningFTPEntryParser() { + return new VMSVersioningFTPEntryParser(); + } + + public FTPFileEntryParser createNetwareFTPEntryParser() { + return new NetwareFTPEntryParser(); + } + + public FTPFileEntryParser createNTFTPEntryParser() { + return createNTFTPEntryParser(null); + } + + /** + * Creates an NT FTP parser: if the config exists, and the system key equals + * {@link FTPClientConfig.SYST_NT} then a plain {@link NTFTPEntryParser} is used, + * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. + * + * @param config the config to use, may be {@code null} + * @return the parser + */ + private FTPFileEntryParser createNTFTPEntryParser(FTPClientConfig config) { + if (config != null && FTPClientConfig.SYST_NT.equals(config.getServerSystemKey())) { + return new NTFTPEntryParser(config); + } else { + // clone the config as it may be changed by the parsers (NET-602) + final FTPClientConfig config2 = (config != null) ? new FTPClientConfig(config) : null; + return new CompositeFileEntryParser(new FTPFileEntryParser[] { + new NTFTPEntryParser(config), new UnixFTPEntryParser(config2, + config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals( + config2.getServerSystemKey())) + }); + } + } + + public FTPFileEntryParser createOS2FTPEntryParser() { + return new OS2FTPEntryParser(); + } + + public FTPFileEntryParser createOS400FTPEntryParser() { + return createOS400FTPEntryParser(null); + } + + /** + * Creates an OS400 FTP parser: if the config exists, and the system key equals + * {@link FTPClientConfig.SYST_OS400} then a plain {@link OS400FTPEntryParser} is used, + * otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. + * + * @param config the config to use, may be {@code null} + * @return the parser + */ + private FTPFileEntryParser createOS400FTPEntryParser(FTPClientConfig config) { + if (config != null && FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) { + return new OS400FTPEntryParser(config); + } else { + // clone the config as it may be changed by the parsers (NET-602) + final FTPClientConfig config2 = (config != null) ? new FTPClientConfig(config) : null; + return new CompositeFileEntryParser(new FTPFileEntryParser[] { + new OS400FTPEntryParser(config), new UnixFTPEntryParser(config2, + config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals( + config2.getServerSystemKey())) + }); + } + } + + public FTPFileEntryParser createMVSEntryParser() { + return new MVSFTPEntryParser(); + } +} + diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java new file mode 100644 index 00000000..ca31583a --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.util.Calendar; + +/** + * Parser for the Connect Enterprise Unix FTP Server From Sterling Commerce. + * Here is a sample of the sort of output line this parser processes: + * "-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST" + *

+ * Note: EnterpriseUnixFTPEntryParser can only be instantiated through the + * DefaultFTPParserFactory by classname. It will not be chosen + * by the autodetection scheme. + * + * + * @version $Id: EnterpriseUnixFTPEntryParser.java 1741829 2016-05-01 00:24:44Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + * @see DefaultFTPFileEntryParserFactory + */ +public class EnterpriseUnixFTPEntryParser extends RegexFTPFileEntryParserImpl { + + /** + * months abbreviations looked for by this parser. Also used + * to determine which month has been matched by the parser. + */ + private static final String MONTHS = "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"; + + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = + "(([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])" + + "([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z]))" + + "(\\S*)\\s*" + // 12 + + "(\\S+)\\s*" + // 13 + + "(\\S*)\\s*" + // 14 user + + "(\\d*)\\s*" + // 15 group + + "(\\d*)\\s*" + // 16 filesize + + MONTHS + // 17 month + + "\\s*" + // TODO should the space be optional? + // TODO \\d* should be \\d? surely ? Otherwise 01111 is allowed + + "((?:[012]\\d*)|(?:3[01]))\\s*" + // 18 date [012]\d* or 3[01] + + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s" + // 20 \d\d\d\d = year OR + // 21 [01]\d or 2[0123] hour + ':' + // 22 [012345]\d = minute + + "(\\S*)(\\s*.*)"; // 23 name + + /** + * The sole constructor for a EnterpriseUnixFTPEntryParser object. + */ + public EnterpriseUnixFTPEntryParser() { + super(REGEX); + } + + /** + * Parses a line of a unix FTP server file listing and converts it into a + * usable format in the form of an FTPFile instance. If + * the file listing line doesn't describe a file, null is + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + + FTPFile file = new FTPFile(); + file.setRawListing(entry); + + if (matches(entry)) { + String usr = group(14); + String grp = group(15); + String filesize = group(16); + String mo = group(17); + String da = group(18); + String yr = group(20); + String hr = group(21); + String min = group(22); + String name = group(23); + + file.setType(FTPFile.FILE_TYPE); + file.setUser(usr); + file.setGroup(grp); + try { + file.setSize(Long.parseLong(filesize)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.HOUR_OF_DAY, 0); + + int pos = MONTHS.indexOf(mo); + int month = pos / 4; + final int missingUnit; // the first missing unit + try { + + if (yr != null) { + // it's a year; there are no hours and minutes + cal.set(Calendar.YEAR, Integer.parseInt(yr)); + missingUnit = Calendar.HOUR_OF_DAY; + } else { + // it must be hour/minute or we wouldn't have matched + missingUnit = Calendar.SECOND; + int year = cal.get(Calendar.YEAR); + + // if the month we're reading is greater than now, it must + // be last year + if (cal.get(Calendar.MONTH) < month) { + year--; + } + cal.set(Calendar.YEAR, year); + cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hr)); + cal.set(Calendar.MINUTE, Integer.parseInt(min)); + } + cal.set(Calendar.MONTH, month); + cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(da)); + cal.clear(missingUnit); + file.setTimestamp(cal); + } catch (NumberFormatException e) { + // do nothing, date will be uninitialized + } + file.setName(name); + + return file; + } + return null; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java new file mode 100644 index 00000000..2e3c84e0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFileEntryParser; + +/** + * The interface describes a factory for creating FTPFileEntryParsers. + * + * @since 1.2 + */ +public interface FTPFileEntryParserFactory { + /** + * Implementation should be a method that decodes the + * supplied key and creates an object implementing the + * interface FTPFileEntryParser. + * + * @param key A string that somehow identifies an + * FTPFileEntryParser to be created. + * @return the FTPFileEntryParser created. + * @throws ParserInitializationException Thrown on any exception in instantiation + */ + public FTPFileEntryParser createFileEntryParser(String key) throws ParserInitializationException; + + /** + *

+ * Implementation should be a method that extracts + * a key from the supplied {@link FTPClientConfig FTPClientConfig} + * parameter and creates an object implementing the + * interface FTPFileEntryParser and uses the supplied configuration + * to configure it. + *

+ * Note that this method will generally not be called in scenarios + * that call for autodetection of parser type but rather, for situations + * where the user knows that the server uses a non-default configuration + * and knows what that configuration is. + *

+ * + * @param config A {@link FTPClientConfig FTPClientConfig} + * used to configure the parser created + * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. + * @throws ParserInitializationException Thrown on any exception in instantiation + * @since 1.4 + */ + public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) + throws ParserInitializationException; +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java new file mode 100644 index 00000000..dab122a4 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import java.text.ParseException; +import java.util.Calendar; + +/** + * This interface specifies the concept of parsing an FTP server's + * timestamp. + * + * @since 1.4 + */ +public interface FTPTimestampParser { + + /** + * the default default date format. + */ + public static final String DEFAULT_SDF = UnixFTPEntryParser.DEFAULT_DATE_FORMAT; + /** + * the default recent date format. + */ + public static final String DEFAULT_RECENT_SDF = UnixFTPEntryParser.DEFAULT_RECENT_DATE_FORMAT; + + /** + * Parses the supplied datestamp parameter. This parameter typically would + * have been pulled from a longer FTP listing via the regular expression + * mechanism + * + * @param timestampStr - the timestamp portion of the FTP directory listing + * to be parsed + * @return a java.util.Calendar object initialized to the date + * parsed by the parser + * @throws ParseException if none of the parser mechanisms belonging to + * the implementor can parse the input. + */ + public Calendar parseTimestamp(String timestampStr) throws ParseException; +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java new file mode 100644 index 00000000..e84209ff --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.Configurable; +import aria.apache.commons.net.ftp.FTPClientConfig; +import java.text.DateFormatSymbols; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * Default implementation of the {@link FTPTimestampParser FTPTimestampParser} + * interface also implements the {@link Configurable Configurable} + * interface to allow the parsing to be configured from the outside. + * + * @see ConfigurableFTPFileEntryParserImpl + * @since 1.4 + */ +public class FTPTimestampParserImpl implements FTPTimestampParser, Configurable { + + /** The date format for all dates, except possibly recent dates. Assumed to include the year. */ + private SimpleDateFormat defaultDateFormat; + /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */ + private int defaultDateSmallestUnitIndex; + + /** The format used for recent dates (which don't have the year). May be null. */ + private SimpleDateFormat recentDateFormat; + /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */ + private int recentDateSmallestUnitIndex; + + private boolean lenientFutureDates = false; + + /* + * List of units in order of increasing significance. + * This allows the code to clear all units in the Calendar until it + * reaches the least significant unit in the parse string. + * The date formats are analysed to find the least significant + * unit (e.g. Minutes or Milliseconds) and the appropriate index to + * the array is saved. + * This is done by searching the array for the unit specifier, + * and returning the index. When clearing the Calendar units, + * the code loops through the array until the previous entry. + * e.g. for MINUTE it would clear MILLISECOND and SECOND + */ + private static final int[] CALENDAR_UNITS = { + Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, + Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR + }; + + /* + * Return the index to the array representing the least significant + * unit found in the date format. + * Default is 0 (to avoid dropping precision) + */ + private static int getEntry(SimpleDateFormat dateFormat) { + if (dateFormat == null) { + return 0; + } + final String FORMAT_CHARS = "SsmHdM"; + final String pattern = dateFormat.toPattern(); + for (char ch : FORMAT_CHARS.toCharArray()) { + if (pattern.indexOf(ch) != -1) { // found the character + switch (ch) { + case 'S': + return indexOf(Calendar.MILLISECOND); + case 's': + return indexOf(Calendar.SECOND); + case 'm': + return indexOf(Calendar.MINUTE); + case 'H': + return indexOf(Calendar.HOUR_OF_DAY); + case 'd': + return indexOf(Calendar.DAY_OF_MONTH); + case 'M': + return indexOf(Calendar.MONTH); + } + } + } + return 0; + } + + /* + * Find the entry in the CALENDAR_UNITS array. + */ + private static int indexOf(int calendarUnit) { + int i; + for (i = 0; i < CALENDAR_UNITS.length; i++) { + if (calendarUnit == CALENDAR_UNITS[i]) { + return i; + } + } + return 0; + } + + /* + * Sets the Calendar precision (used by FTPFile#toFormattedDate) by clearing + * the immediately preceeding unit (if any). + * Unfortunately the clear(int) method results in setting all other units. + */ + private static void setPrecision(int index, Calendar working) { + if (index <= 0) { // e.g. MILLISECONDS + return; + } + final int field = CALENDAR_UNITS[index - 1]; + // Just in case the analysis is wrong, stop clearing if + // field value is not the default. + final int value = working.get(field); + if (value != 0) { // don't reset if it has a value + // new Throwable("Unexpected value "+value).printStackTrace(); // DEBUG + } else { + working.clear(field); // reset just the required field + } + } + + /** + * The only constructor for this class. + */ + public FTPTimestampParserImpl() { + setDefaultDateFormat(DEFAULT_SDF, null); + setRecentDateFormat(DEFAULT_RECENT_SDF, null); + } + + /** + * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} + * in the {@link FTPTimestampParser FTPTimestampParser} interface + * according to this algorithm: + * + * If the recentDateFormat member has been defined, try to parse the + * supplied string with that. If that parse fails, or if the recentDateFormat + * member has not been defined, attempt to parse with the defaultDateFormat + * member. If that fails, throw a ParseException. + * + * This method assumes that the server time is the same as the local time. + * + * @param timestampStr The timestamp to be parsed + * @return a Calendar with the parsed timestamp + * @see FTPTimestampParserImpl#parseTimestamp(String, Calendar) + */ + @Override public Calendar parseTimestamp(String timestampStr) throws ParseException { + Calendar now = Calendar.getInstance(); + return parseTimestamp(timestampStr, now); + } + + /** + * If the recentDateFormat member has been defined, try to parse the + * supplied string with that. If that parse fails, or if the recentDateFormat + * member has not been defined, attempt to parse with the defaultDateFormat + * member. If that fails, throw a ParseException. + * + * This method allows a {@link Calendar} instance to be passed in which represents the + * current (system) time. + * + * @param timestampStr The timestamp to be parsed + * @param serverTime The current time for the server + * @return the calendar + * @throws ParseException if timestamp cannot be parsed + * @see FTPTimestampParser#parseTimestamp(String) + * @since 1.5 + */ + public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException { + Calendar working = (Calendar) serverTime.clone(); + working.setTimeZone(getServerTimeZone()); // is this needed? + + Date parsed = null; + + if (recentDateFormat != null) { + Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it + now.setTimeZone(this.getServerTimeZone()); + if (lenientFutureDates) { + // add a day to "now" so that "slop" doesn't cause a date + // slightly in the future to roll back a full year. (Bug 35181 => NET-83) + now.add(Calendar.DAY_OF_MONTH, 1); + } + // The Java SimpleDateFormat class uses the epoch year 1970 if not present in the input + // As 1970 was not a leap year, it cannot parse "Feb 29" correctly. + // Java 1.5+ returns Mar 1 1970 + // Temporarily add the current year to the short date time + // to cope with short-date leap year strings. + // Since Feb 29 is more that 6 months from the end of the year, this should be OK for + // all instances of short dates which are +- 6 months from current date. + // TODO this won't always work for systems that use short dates +0/-12months + // e.g. if today is Jan 1 2001 and the short date is Feb 29 + String year = Integer.toString(now.get(Calendar.YEAR)); + String timeStampStrPlusYear = timestampStr + " " + year; + SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy", + recentDateFormat.getDateFormatSymbols()); + hackFormatter.setLenient(false); + hackFormatter.setTimeZone(recentDateFormat.getTimeZone()); + ParsePosition pp = new ParsePosition(0); + parsed = hackFormatter.parse(timeStampStrPlusYear, pp); + // Check if we parsed the full string, if so it must have been a short date originally + if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) { + working.setTime(parsed); + if (working.after(now)) { // must have been last year instead + working.add(Calendar.YEAR, -1); + } + setPrecision(recentDateSmallestUnitIndex, working); + return working; + } + } + + ParsePosition pp = new ParsePosition(0); + parsed = defaultDateFormat.parse(timestampStr, pp); + // note, length checks are mandatory for us since + // SimpleDateFormat methods will succeed if less than + // full string is matched. They will also accept, + // despite "leniency" setting, a two-digit number as + // a valid year (e.g. 22:04 will parse as 22 A.D.) + // so could mistakenly confuse an hour with a year, + // if we don't insist on full length parsing. + if (parsed != null && pp.getIndex() == timestampStr.length()) { + working.setTime(parsed); + } else { + throw new ParseException("Timestamp '" + + timestampStr + + "' could not be parsed using a server time of " + + serverTime.getTime().toString(), pp.getErrorIndex()); + } + setPrecision(defaultDateSmallestUnitIndex, working); + return working; + } + + /** + * @return Returns the defaultDateFormat. + */ + public SimpleDateFormat getDefaultDateFormat() { + return defaultDateFormat; + } + + /** + * @return Returns the defaultDateFormat pattern string. + */ + public String getDefaultDateFormatString() { + return defaultDateFormat.toPattern(); + } + + /** + * @param format The defaultDateFormat to be set. + * @param dfs the symbols to use (may be null) + */ + private void setDefaultDateFormat(String format, DateFormatSymbols dfs) { + if (format != null) { + if (dfs != null) { + this.defaultDateFormat = new SimpleDateFormat(format, dfs); + } else { + this.defaultDateFormat = new SimpleDateFormat(format); + } + this.defaultDateFormat.setLenient(false); + } else { + this.defaultDateFormat = null; + } + this.defaultDateSmallestUnitIndex = getEntry(this.defaultDateFormat); + } + + /** + * @return Returns the recentDateFormat. + */ + public SimpleDateFormat getRecentDateFormat() { + return recentDateFormat; + } + + /** + * @return Returns the recentDateFormat. + */ + public String getRecentDateFormatString() { + return recentDateFormat.toPattern(); + } + + /** + * @param format The recentDateFormat to set. + * @param dfs the symbols to use (may be null) + */ + private void setRecentDateFormat(String format, DateFormatSymbols dfs) { + if (format != null) { + if (dfs != null) { + this.recentDateFormat = new SimpleDateFormat(format, dfs); + } else { + this.recentDateFormat = new SimpleDateFormat(format); + } + this.recentDateFormat.setLenient(false); + } else { + this.recentDateFormat = null; + } + this.recentDateSmallestUnitIndex = getEntry(this.recentDateFormat); + } + + /** + * @return returns an array of 12 strings representing the short + * month names used by this parse. + */ + public String[] getShortMonths() { + return defaultDateFormat.getDateFormatSymbols().getShortMonths(); + } + + /** + * @return Returns the serverTimeZone used by this parser. + */ + public TimeZone getServerTimeZone() { + return this.defaultDateFormat.getTimeZone(); + } + + /** + * sets a TimeZone represented by the supplied ID string into all + * of the parsers used by this server. + * + * @param serverTimeZone Time Id java.util.TimeZone id used by + * the ftp server. If null the client's local time zone is assumed. + */ + private void setServerTimeZone(String serverTimeZoneId) { + TimeZone serverTimeZone = TimeZone.getDefault(); + if (serverTimeZoneId != null) { + serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId); + } + this.defaultDateFormat.setTimeZone(serverTimeZone); + if (this.recentDateFormat != null) { + this.recentDateFormat.setTimeZone(serverTimeZone); + } + } + + /** + * Implementation of the {@link Configurable Configurable} + * interface. Configures this FTPTimestampParser according + * to the following logic: + *

+ * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(String) defaultDateFormat} + * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} + * to values supplied in the config based on month names configured as follows: + *

+ *
    + *
  • If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString} + * has been supplied in the config, use that to parse parse timestamps.
  • + *
  • Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode} + * has been supplied in the config, use the month names represented + * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language} + * to parse timestamps.
  • + *
  • otherwise use default English month names
  • + *

+ * Finally if a {@link FTPClientConfig#setServerTimeZoneId(String) + * serverTimeZoneId} + * has been supplied via the config, set that into all date formats that have + * been configured. + *

+ */ + @Override public void configure(FTPClientConfig config) { + DateFormatSymbols dfs = null; + + String languageCode = config.getServerLanguageCode(); + String shortmonths = config.getShortMonthNames(); + if (shortmonths != null) { + dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); + } else if (languageCode != null) { + dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); + } else { + dfs = FTPClientConfig.lookupDateFormatSymbols("en"); + } + + String recentFormatString = config.getRecentDateFormatStr(); + setRecentDateFormat(recentFormatString, dfs); + + String defaultFormatString = config.getDefaultDateFormatStr(); + if (defaultFormatString == null) { + throw new IllegalArgumentException("defaultFormatString cannot be null"); + } + setDefaultDateFormat(defaultFormatString, dfs); + + setServerTimeZone(config.getServerTimeZoneId()); + + this.lenientFutureDates = config.isLenientFutureDates(); + } + + /** + * @return Returns the lenientFutureDates. + */ + boolean isLenientFutureDates() { + return lenientFutureDates; + } + + /** + * @param lenientFutureDates The lenientFutureDates to set. + */ + void setLenientFutureDates(boolean lenientFutureDates) { + this.lenientFutureDates = lenientFutureDates; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java new file mode 100644 index 00000000..b6d11bb8 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java @@ -0,0 +1,260 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Locale; +import java.util.TimeZone; + +/** + * Parser class for MSLT and MLSD replies. See RFC 3659. + *

+ * Format is as follows: + *

+ * entry            = [ facts ] SP pathname
+ * facts            = 1*( fact ";" )
+ * fact             = factname "=" value
+ * factname         = "Size" / "Modify" / "Create" /
+ *                    "Type" / "Unique" / "Perm" /
+ *                    "Lang" / "Media-Type" / "CharSet" /
+ * os-depend-fact / local-fact
+ * os-depend-fact   = {IANA assigned OS name} "." token
+ * local-fact       = "X." token
+ * value            = *SCHAR
+ *
+ * Sample os-depend-fact:
+ * UNIX.group=0;UNIX.mode=0755;UNIX.owner=0;
+ * 
+ * A single control response entry (MLST) is returned with a leading space; + * multiple (data) entries are returned without any leading spaces. + * The parser requires that the leading space from the MLST entry is removed. + * MLSD entries can begin with a single space if there are no facts. + * + * @since 3.0 + */ +public class MLSxEntryParser extends FTPFileEntryParserImpl { + // This class is immutable, so a single instance can be shared. + private static final MLSxEntryParser PARSER = new MLSxEntryParser(); + + private static final HashMap TYPE_TO_INT = new HashMap(); + + static { + TYPE_TO_INT.put("file", Integer.valueOf(FTPFile.FILE_TYPE)); + TYPE_TO_INT.put("cdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // listed directory + TYPE_TO_INT.put("pdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // a parent dir + TYPE_TO_INT.put("dir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // dir or sub-dir + } + + private static int UNIX_GROUPS[] = { // Groups in order of mode digits + FTPFile.USER_ACCESS, FTPFile.GROUP_ACCESS, FTPFile.WORLD_ACCESS, + }; + + private static int UNIX_PERMS[][] = { // perm bits, broken down by octal int value +/* 0 */ {}, +/* 1 */ { FTPFile.EXECUTE_PERMISSION }, +/* 2 */ { FTPFile.WRITE_PERMISSION }, +/* 3 */ { FTPFile.EXECUTE_PERMISSION, FTPFile.WRITE_PERMISSION }, +/* 4 */ { FTPFile.READ_PERMISSION }, +/* 5 */ { FTPFile.READ_PERMISSION, FTPFile.EXECUTE_PERMISSION }, +/* 6 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION }, +/* 7 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION, FTPFile.EXECUTE_PERMISSION }, + }; + + /** + * Create the parser for MSLT and MSLD listing entries + * This class is immutable, so one can use {@link #getInstance()} instead. + */ + public MLSxEntryParser() { + super(); + } + + @Override public FTPFile parseFTPEntry(String entry) { + if (entry.startsWith(" ")) {// leading space means no facts are present + if (entry.length() > 1) { // is there a path name? + FTPFile file = new FTPFile(); + file.setRawListing(entry); + file.setName(entry.substring(1)); + return file; + } else { + return null; // Invalid - no pathname + } + } + String parts[] = entry.split(" ", 2); // Path may contain space + if (parts.length != 2 || parts[1].length() == 0) { + return null; // no space found or no file name + } + final String factList = parts[0]; + if (!factList.endsWith(";")) { + return null; + } + FTPFile file = new FTPFile(); + file.setRawListing(entry); + file.setName(parts[1]); + String[] facts = factList.split(";"); + boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode="); + for (String fact : facts) { + String[] factparts = fact.split("=", -1); // Don't drop empty values + // Sample missing permission + // drwx------ 2 mirror mirror 4096 Mar 13 2010 subversion + // modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion + if (factparts.length != 2) { + return null; // invalid - there was no "=" sign + } + String factname = factparts[0].toLowerCase(Locale.ENGLISH); + String factvalue = factparts[1]; + if (factvalue.length() == 0) { + continue; // nothing to see here + } + String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH); + if ("size".equals(factname)) { + file.setSize(Long.parseLong(factvalue)); + } else if ("sizd".equals(factname)) { // Directory size + file.setSize(Long.parseLong(factvalue)); + } else if ("modify".equals(factname)) { + final Calendar parsed = parseGMTdateTime(factvalue); + if (parsed == null) { + return null; + } + file.setTimestamp(parsed); + } else if ("type".equals(factname)) { + Integer intType = TYPE_TO_INT.get(valueLowerCase); + if (intType == null) { + file.setType(FTPFile.UNKNOWN_TYPE); + } else { + file.setType(intType.intValue()); + } + } else if (factname.startsWith("unix.")) { + String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH); + if ("group".equals(unixfact)) { + file.setGroup(factvalue); + } else if ("owner".equals(unixfact)) { + file.setUser(factvalue); + } else if ("mode".equals(unixfact)) { // e.g. 0[1]755 + int off = factvalue.length() - 3; // only parse last 3 digits + for (int i = 0; i < 3; i++) { + int ch = factvalue.charAt(off + i) - '0'; + if (ch >= 0 && ch <= 7) { // Check it's valid octal + for (int p : UNIX_PERMS[ch]) { + file.setPermission(UNIX_GROUPS[i], p, true); + } + } else { + // TODO should this cause failure, or can it be reported somehow? + } + } // digits + } // mode + } // unix. + else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode + doUnixPerms(file, valueLowerCase); + } // process "perm" + } // each fact + return file; + } + + /** + * Parse a GMT time stamp of the form YYYYMMDDHHMMSS[.sss] + * + * @param timestamp the date-time to parse + * @return a Calendar entry, may be {@code null} + * @since 3.4 + */ + public static Calendar parseGMTdateTime(String timestamp) { + final SimpleDateFormat sdf; + final boolean hasMillis; + if (timestamp.contains(".")) { + sdf = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); + hasMillis = true; + } else { + sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + hasMillis = false; + } + TimeZone GMT = TimeZone.getTimeZone("GMT"); + // both timezones need to be set for the parse to work OK + sdf.setTimeZone(GMT); + GregorianCalendar gc = new GregorianCalendar(GMT); + ParsePosition pos = new ParsePosition(0); + sdf.setLenient(false); // We want to parse the whole string + final Date parsed = sdf.parse(timestamp, pos); + if (pos.getIndex() != timestamp.length()) { + return null; // did not fully parse the input + } + gc.setTime(parsed); + if (!hasMillis) { + gc.clear(Calendar.MILLISECOND); // flag up missing ms units + } + return gc; + } + + // perm-fact = "Perm" "=" *pvals + // pvals = "a" / "c" / "d" / "e" / "f" / + // "l" / "m" / "p" / "r" / "w" + private void doUnixPerms(FTPFile file, String valueLowerCase) { + for (char c : valueLowerCase.toCharArray()) { + // TODO these are mostly just guesses at present + switch (c) { + case 'a': // (file) may APPEnd + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'c': // (dir) files may be created in the dir + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'd': // deletable + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'e': // (dir) can change to this dir + file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); + break; + case 'f': // (file) renamable + // ?? file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'l': // (dir) can be listed + file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION, true); + break; + case 'm': // (dir) can create directory here + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'p': // (dir) entries may be deleted + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + case 'r': // (files) file may be RETRieved + file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); + break; + case 'w': // (files) file may be STORed + file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + break; + default: + break; + // ignore unexpected flag for now. + } // switch + } // each char + } + + public static FTPFile parseEntry(String entry) { + return PARSER.parseFTPEntry(entry); + } + + public static MLSxEntryParser getInstance() { + return PARSER; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java new file mode 100644 index 00000000..85a01925 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java @@ -0,0 +1,537 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; +import java.util.List; + +/** + * Implementation of FTPFileEntryParser and FTPFileListParser for IBM zOS/MVS + * Systems. + * + * @version $Id: MVSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for + * usage instructions) + */ +public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + static final int UNKNOWN_LIST_TYPE = -1; + static final int FILE_LIST_TYPE = 0; + static final int MEMBER_LIST_TYPE = 1; + static final int UNIX_LIST_TYPE = 2; + static final int JES_LEVEL_1_LIST_TYPE = 3; + static final int JES_LEVEL_2_LIST_TYPE = 4; + + private int isType = UNKNOWN_LIST_TYPE; + + /** + * Fallback parser for Unix-style listings + */ + private UnixFTPEntryParser unixFTPEntryParser; + + /** + * Dates are ignored for file lists, but are used for member lists where + * possible + */ + static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm"; // 2001/09/18 + // 13:52 + + /** + * Matches these entries: + *
+   *  Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
+   *  B10142 3390   2006/03/20  2   31  F       80    80  PS   MDI.OKL.WORK
+   * 
+ */ + static final String FILE_LIST_REGEX = "\\S+\\s+" + // volume + // ignored + "\\S+\\s+" + // unit - ignored + "\\S+\\s+" + // access date - ignored + "\\S+\\s+" + // extents -ignored + "\\S+\\s+" + // used - ignored + "[FV]\\S*\\s+" + // recfm - must start with F or V + "\\S+\\s+" + // logical record length -ignored + "\\S+\\s+" + // block size - ignored + "(PS|PO|PO-E)\\s+" + // Dataset organisation. Many exist + // but only support: PS, PO, PO-E + "(\\S+)\\s*"; // Dataset Name (file name) + + /** + * Matches these entries: + *
+   *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
+   *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
+   * 
+ */ + static final String MEMBER_LIST_REGEX = "(\\S+)\\s+" + // name + "\\S+\\s+" + // version, modification (ignored) + "\\S+\\s+" + // create date (ignored) + "(\\S+)\\s+" + // modification date + "(\\S+)\\s+" + // modification time + "\\S+\\s+" + // size in lines (ignored) + "\\S+\\s+" + // size in lines at creation(ignored) + "\\S+\\s+" + // lines modified (ignored) + "\\S+\\s*"; // id of user who modified (ignored) + + /** + * Matches these entries, note: no header: + *
+   *   IBMUSER1  JOB01906  OUTPUT    3 Spool Files
+   *   012345678901234567890123456789012345678901234
+   *             1         2         3         4
+   * 
+ */ + static final String JES_LEVEL_1_LIST_REGEX = "(\\S+)\\s+" + // job name ignored + "(\\S+)\\s+" + // job number + "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) + "(\\S+)\\s+" + // number of spool files + "(\\S+)\\s+" + // Text "Spool" ignored + "(\\S+)\\s*" // Text "Files" ignored + ; + + /** + * JES INTERFACE LEVEL 2 parser + * Matches these entries: + *
+   * JOBNAME  JOBID    OWNER    STATUS CLASS
+   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
+   * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
+   * 
+ * Sample output from FTP session: + *
+   * ftp> quote site filetype=jes
+   * 200 SITE command was accepted
+   * ftp> ls
+   * 200 Port request OK.
+   * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
+   * JOBNAME  JOBID    OWNER    STATUS CLASS
+   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
+   * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
+   * 250 List completed successfully.
+   * ftp> ls job01906
+   * 200 Port request OK.
+   * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
+   * JOBNAME  JOBID    OWNER    STATUS CLASS
+   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000
+   * --------
+   * ID  STEPNAME PROCSTEP C DDNAME   BYTE-COUNT
+   * 001 JES2              A JESMSGLG       858
+   * 002 JES2              A JESJCL         128
+   * 003 JES2              A JESYSMSG       443
+   * 3 spool files
+   * 250 List completed successfully.
+   * 
+ */ + + static final String JES_LEVEL_2_LIST_REGEX = "(\\S+)\\s+" + // job name ignored + "(\\S+)\\s+" + // job number + "(\\S+)\\s+" + // owner ignored + "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) ignored + "(\\S+)\\s+" + // job class ignored + "(\\S+).*" // rest ignored + ; + + /* + * --------------------------------------------------------------------- + * Very brief and incomplete description of the zOS/MVS-filesystem. (Note: + * "zOS" is the operating system on the mainframe, and is the new name for + * MVS) + * + * The filesystem on the mainframe does not have hierarchal structure as for + * example the unix filesystem. For a more comprehensive description, please + * refer to the IBM manuals + * + * @LINK: + * http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/dgt2d440/CONTENTS + * + * + * Dataset names ============= + * + * A dataset name consist of a number of qualifiers separated by '.', each + * qualifier can be at most 8 characters, and the total length of a dataset + * can be max 44 characters including the dots. + * + * + * Dataset organisation ==================== + * + * A dataset represents a piece of storage allocated on one or more disks. + * The structure of the storage is described with the field dataset + * organinsation (DSORG). There are a number of dataset organisations, but + * only two are usable for FTP transfer. + * + * DSORG: PS: sequential, or flat file PO: partitioned dataset PO-E: + * extended partitioned dataset + * + * The PS file is just a flat file, as you would find it on the unix file + * system. + * + * The PO and PO-E files, can be compared to a single level directory + * structure. A PO file consist of a number of dataset members, or files if + * you will. It is possible to CD into the file, and to retrieve the + * individual members. + * + * + * Dataset record format ===================== + * + * The physical layout of the dataset is described on the dataset itself. + * There are a number of record formats (RECFM), but just a few is relavant + * for the FTP transfer. + * + * Any one beginning with either F or V can safely used by FTP transfer. All + * others should only be used with great care, so this version will just + * ignore the other record formats. F means a fixed number of records per + * allocated storage, and V means a variable number of records. + * + * + * Other notes =========== + * + * The file system supports automatically backup and retrieval of datasets. + * If a file is backed up, the ftp LIST command will return: ARCIVE Not + * Direct Access Device KJ.IOP998.ERROR.PL.UNITTEST + * + * + * Implementation notes ==================== + * + * Only datasets that have dsorg PS, PO or PO-E and have recfm beginning + * with F or V, is fully parsed. + * + * The following fields in FTPFile is used: FTPFile.Rawlisting: Always set. + * FTPFile.Type: DIRECTORY_TYPE or FILE_TYPE or UNKNOWN FTPFile.Name: name + * FTPFile.Timestamp: change time or null + * + * + * + * Additional information ====================== + * + * The MVS ftp server supports a number of features via the FTP interface. + * The features are controlled with the FTP command quote site filetype= + * SEQ is the default and used for normal file transfer JES is used to + * interact with the Job Entry Subsystem (JES) similar to a job scheduler + * DB2 is used to interact with a DB2 subsystem + * + * This parser supports SEQ and JES. + * + * + * + * + * + * + */ + + /** + * The sole constructor for a MVSFTPEntryParser object. + */ + public MVSFTPEntryParser() { + super(""); // note the regex is set in preParse. + super.configure(null); // configure parser with default configurations + } + + /** + * Parses a line of an z/OS - MVS FTP server file listing and converts it + * into a usable format in the form of an FTPFile instance. + * If the file listing line doesn't describe a file, then + * null is returned. Otherwise a FTPFile + * instance representing the file is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + boolean isParsed = false; + FTPFile f = new FTPFile(); + + if (isType == FILE_LIST_TYPE) { + isParsed = parseFileList(f, entry); + } else if (isType == MEMBER_LIST_TYPE) { + isParsed = parseMemberList(f, entry); + if (!isParsed) { + isParsed = parseSimpleEntry(f, entry); + } + } else if (isType == UNIX_LIST_TYPE) { + isParsed = parseUnixList(f, entry); + } else if (isType == JES_LEVEL_1_LIST_TYPE) { + isParsed = parseJeslevel1List(f, entry); + } else if (isType == JES_LEVEL_2_LIST_TYPE) { + isParsed = parseJeslevel2List(f, entry); + } + + if (!isParsed) { + f = null; + } + + return f; + } + + /** + * Parse entries representing a dataset list. Only datasets with DSORG PS or + * PO or PO-E and with RECFM F* or V* will be parsed. + * + * Format of ZOS/MVS file list: 1 2 3 4 5 6 7 8 9 10 Volume Unit Referred + * Ext Used Recfm Lrecl BlkSz Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80 + * 80 PS MDI.OKL.WORK ARCIVE Not Direct Access Device + * KJ.IOP998.ERROR.PL.UNITTEST B1N231 3390 2006/03/20 1 15 VB 256 27998 PO + * PLU B1N231 3390 2006/03/20 1 15 VB 256 27998 PO-E PLB + * + * ----------------------------------- Group within Regex [1] Volume [2] + * Unit [3] Referred [4] Ext: number of extents [5] Used [6] Recfm: Record + * format [7] Lrecl: Logical record length [8] BlkSz: Block size [9] Dsorg: + * Dataset organisation. Many exists but only support: PS, PO, PO-E [10] + * Dsname: Dataset name + * + * Note: When volume is ARCIVE, it means the dataset is stored somewhere in + * a tape archive. These entries is currently not supported by this parser. + * A null value is returned. + * + * @param file will be updated with Name, Type, Timestamp if parsed. + * @param entry zosDirectoryEntry + * @return true: entry was parsed, false: entry was not parsed. + */ + private boolean parseFileList(FTPFile file, String entry) { + if (matches(entry)) { + file.setRawListing(entry); + String name = group(2); + String dsorg = group(1); + file.setName(name); + + // DSORG + if ("PS".equals(dsorg)) { + file.setType(FTPFile.FILE_TYPE); + } else if ("PO".equals(dsorg) || "PO-E".equals(dsorg)) { + // regex already ruled out anything other than PO or PO-E + file.setType(FTPFile.DIRECTORY_TYPE); + } else { + return false; + } + + return true; + } + + return false; + } + + /** + * Parse entries within a partitioned dataset. + * + * Format of a memberlist within a PDS: + *
+   *    0         1        2          3        4     5     6      7    8
+   *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
+   *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
+   *   TBTOOL    01.12 2002/09/12 2004/11/26 19:54    51    28     0 KIL001
+   *
+   * -------------------------------------------
+   * [1] Name
+   * [2] VV.MM: Version . modification
+   * [3] Created: yyyy / MM / dd
+   * [4,5] Changed: yyyy / MM / dd HH:mm
+   * [6] Size: number of lines
+   * [7] Init: number of lines when first created
+   * [8] Mod: number of modified lines a last save
+   * [9] Id: User id for last update
+   * 
+ * + * @param file will be updated with Name, Type and Timestamp if parsed. + * @param entry zosDirectoryEntry + * @return true: entry was parsed, false: entry was not parsed. + */ + private boolean parseMemberList(FTPFile file, String entry) { + if (matches(entry)) { + file.setRawListing(entry); + String name = group(1); + String datestr = group(2) + " " + group(3); + file.setName(name); + file.setType(FTPFile.FILE_TYPE); + try { + file.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + e.printStackTrace(); + // just ignore parsing errors. + // TODO check this is ok + return false; // this is a parsing failure too. + } + return true; + } + + return false; + } + + /** + * Assigns the name to the first word of the entry. Only to be used from a + * safe context, for example from a memberlist, where the regex for some + * reason fails. Then just assign the name field of FTPFile. + * + * @return true if the entry string is non-null and non-empty + */ + private boolean parseSimpleEntry(FTPFile file, String entry) { + if (entry != null && entry.trim().length() > 0) { + file.setRawListing(entry); + String name = entry.split(" ")[0]; + file.setName(name); + file.setType(FTPFile.FILE_TYPE); + return true; + } + return false; + } + + /** + * Parse the entry as a standard unix file. Using the UnixFTPEntryParser. + * + * @return true: entry is parsed, false: entry could not be parsed. + */ + private boolean parseUnixList(FTPFile file, String entry) { + file = unixFTPEntryParser.parseFTPEntry(entry); + if (file == null) { + return false; + } + return true; + } + + /** + * Matches these entries, note: no header: + *
+   * [1]      [2]      [3]   [4] [5]
+   * IBMUSER1 JOB01906 OUTPUT 3 Spool Files
+   * 012345678901234567890123456789012345678901234
+   *           1         2         3         4
+   * -------------------------------------------
+   * Group in regex
+   * [1] Job name
+   * [2] Job number
+   * [3] Job status (INPUT,ACTIVE,OUTPUT)
+   * [4] Number of sysout files
+   * [5] The string "Spool Files"
+   * 
+ * + * @param file will be updated with Name, Type and Timestamp if parsed. + * @param entry zosDirectoryEntry + * @return true: entry was parsed, false: entry was not parsed. + */ + private boolean parseJeslevel1List(FTPFile file, String entry) { + if (matches(entry)) { + if (group(3).equalsIgnoreCase("OUTPUT")) { + file.setRawListing(entry); + String name = group(2); /* Job Number, used by GET */ + file.setName(name); + file.setType(FTPFile.FILE_TYPE); + return true; + } + } + + return false; + } + + /** + * Matches these entries: + *
+   * [1]      [2]      [3]     [4]    [5]
+   * JOBNAME  JOBID    OWNER   STATUS CLASS
+   * IBMUSER1 JOB01906 IBMUSER OUTPUT A       RC=0000 3 spool files
+   * IBMUSER  TSU01830 IBMUSER OUTPUT TSU     ABEND=522 3 spool files
+   * 012345678901234567890123456789012345678901234
+   *           1         2         3         4
+   * -------------------------------------------
+   * Group in regex
+   * [1] Job name
+   * [2] Job number
+   * [3] Owner
+   * [4] Job status (INPUT,ACTIVE,OUTPUT)
+   * [5] Job Class
+   * [6] The rest
+   * 
+ * + * @param file will be updated with Name, Type and Timestamp if parsed. + * @param entry zosDirectoryEntry + * @return true: entry was parsed, false: entry was not parsed. + */ + private boolean parseJeslevel2List(FTPFile file, String entry) { + if (matches(entry)) { + if (group(4).equalsIgnoreCase("OUTPUT")) { + file.setRawListing(entry); + String name = group(2); /* Job Number, used by GET */ + file.setName(name); + file.setType(FTPFile.FILE_TYPE); + return true; + } + } + + return false; + } + + /** + * preParse is called as part of the interface. Per definition is is called + * before the parsing takes place. + * Three kind of lists is recognize: + * z/OS-MVS File lists + * z/OS-MVS Member lists + * unix file lists + * + * @since 2.0 + */ + @Override public List preParse(List orig) { + // simply remove the header line. Composite logic will take care of the + // two different types of + // list in short order. + if (orig != null && orig.size() > 0) { + String header = orig.get(0); + if (header.indexOf("Volume") >= 0 && header.indexOf("Dsname") >= 0) { + setType(FILE_LIST_TYPE); + super.setRegex(FILE_LIST_REGEX); + } else if (header.indexOf("Name") >= 0 && header.indexOf("Id") >= 0) { + setType(MEMBER_LIST_TYPE); + super.setRegex(MEMBER_LIST_REGEX); + } else if (header.indexOf("total") == 0) { + setType(UNIX_LIST_TYPE); + unixFTPEntryParser = new UnixFTPEntryParser(); + } else if (header.indexOf("Spool Files") >= 30) { + setType(JES_LEVEL_1_LIST_TYPE); + super.setRegex(JES_LEVEL_1_LIST_REGEX); + } else if (header.indexOf("JOBNAME") == 0 + && header.indexOf("JOBID") > 8) {// header contains JOBNAME JOBID OWNER // STATUS CLASS + setType(JES_LEVEL_2_LIST_TYPE); + super.setRegex(JES_LEVEL_2_LIST_REGEX); + } else { + setType(UNKNOWN_LIST_TYPE); + } + + if (isType != JES_LEVEL_1_LIST_TYPE) { // remove header is necessary + orig.remove(0); + } + } + + return orig; + } + + /** + * Explicitly set the type of listing being processed. + * + * @param type The listing type. + */ + void setType(int type) { + isType = type; + } + + /* + * @return + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_MVS, DEFAULT_DATE_FORMAT, null); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java new file mode 100644 index 00000000..2c8414d6 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; + +/** + * Implementation FTPFileEntryParser and FTPFileListParser for pre MacOS-X Systems. + * + * @version $Id: MacOsPeterFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + * @since 3.1 + */ +public class +MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; //Nov 9 2001 + + static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; //Nov 9 20:06 + + /** + * this is the regular expression used by this parser. + * + * Permissions: + * r the file is readable + * w the file is writable + * x the file is executable + * - the indicated permission is not granted + * L mandatory locking occurs during access (the set-group-ID bit is + * on and the group execution bit is off) + * s the set-user-ID or set-group-ID bit is on, and the corresponding + * user or group execution bit is also on + * S undefined bit-state (the set-user-ID bit is on and the user + * execution bit is off) + * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and + * execution is on + * T the 1000 bit is turned on, and execution is off (undefined bit- + * state) + * e z/OS external link bit + */ + private static final String REGEX = "([bcdelfmpSs-])" + // type (1) + + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" + // permission + + "(" + + "(folder\\s+)" + + "|" + + "((\\d+)\\s+(\\d+)\\s+)" + // resource size & data size + + ")" + + "(\\d+)\\s+" + // size + /* + * numeric or standard format date: + * yyyy-mm-dd (expecting hh:mm to follow) + * MMM [d]d + * [d]d MMM + * N.B. use non-space for MMM to allow for languages such as German which use + * diacritics (e.g. umlaut) in some abbreviations. + */ + + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+" + /* + year (for non-recent standard format) - yyyy + or time (for numeric or recent standard format) [h]h:mm + */ + + "(\\d+(?::\\d+)?)\\s+" + + + "(\\S*)(\\s*.*)"; // the rest + + /** + * The default constructor for a UnixFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public MacOsPeterFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of a UnixFTPEntryParser object with + * something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public MacOsPeterFTPEntryParser(FTPClientConfig config) { + super(REGEX); + configure(config); + } + + /** + * Parses a line of a unix (standard) FTP server file listing and converts + * it into a usable format in the form of an FTPFile + * instance. If the file listing line doesn't describe a file, + * null is returned, otherwise a FTPFile + * instance representing the files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + FTPFile file = new FTPFile(); + file.setRawListing(entry); + int type; + boolean isDevice = false; + + if (matches(entry)) { + String typeStr = group(1); + String hardLinkCount = "0"; + String usr = null; + String grp = null; + String filesize = group(20); + String datestr = group(21) + " " + group(22); + String name = group(23); + String endtoken = group(24); + + try { + file.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + // A 'whiteout' file is an ARTIFICIAL entry in any of several types of + // 'translucent' filesystems, of which a 'union' filesystem is one. + + // bcdelfmpSs- + switch (typeStr.charAt(0)) { + case 'd': + type = FTPFile.DIRECTORY_TYPE; + break; + case 'e': // NET-39 => z/OS external link + type = FTPFile.SYMBOLIC_LINK_TYPE; + break; + case 'l': + type = FTPFile.SYMBOLIC_LINK_TYPE; + break; + case 'b': + case 'c': + isDevice = true; + type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented + break; + case 'f': + case '-': + type = FTPFile.FILE_TYPE; + break; + default: // e.g. ? and w = whiteout + type = FTPFile.UNKNOWN_TYPE; + } + + file.setType(type); + + int g = 4; + for (int access = 0; access < 3; access++, g += 4) { + // Use != '-' to avoid having to check for suid and sticky bits + file.setPermission(access, FTPFile.READ_PERMISSION, (!group(g).equals("-"))); + file.setPermission(access, FTPFile.WRITE_PERMISSION, (!group(g + 1).equals("-"))); + + String execPerm = group(g + 2); + if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) { + file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); + } else { + file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); + } + } + + if (!isDevice) { + try { + file.setHardLinkCount(Integer.parseInt(hardLinkCount)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + } + + file.setUser(usr); + file.setGroup(grp); + + try { + file.setSize(Long.parseLong(filesize)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + + if (null == endtoken) { + file.setName(name); + } else { + // oddball cases like symbolic links, file names + // with spaces in them. + name += endtoken; + if (type == FTPFile.SYMBOLIC_LINK_TYPE) { + + int end = name.indexOf(" -> "); + // Give up if no link indicator is present + if (end == -1) { + file.setName(name); + } else { + file.setName(name.substring(0, end)); + file.setLink(name.substring(end + 4)); + } + } else { + file.setName(name); + } + } + return file; + } + return null; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, + DEFAULT_RECENT_DATE_FORMAT); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java new file mode 100644 index 00000000..cee2811a --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.Configurable; +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; +import java.util.regex.Pattern; + +/** + * Implementation of FTPFileEntryParser and FTPFileListParser for NT Systems. + * + * @version $Id: NTFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy hh:mma"; //11-09-01 12:30PM + + private static final String DEFAULT_DATE_FORMAT2 = "MM-dd-yy kk:mm"; //11-09-01 18:30 + + private final FTPTimestampParser timestampParser; + + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = + "(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma|kk:mm; swallow trailing spaces + + "(?:()|([0-9]+))\\s+" // or ddddd; swallow trailing spaces + + "(\\S.*)"; // First non-space followed by rest of line (name) + + /** + * The sole constructor for an NTFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public NTFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of an NTFTPEntryParser object + * with something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public NTFTPEntryParser(FTPClientConfig config) { + super(REGEX, Pattern.DOTALL); + configure(config); + FTPClientConfig config2 = + new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT2, null); + config2.setDefaultDateFormatStr(DEFAULT_DATE_FORMAT2); + this.timestampParser = new FTPTimestampParserImpl(); + ((Configurable) this.timestampParser).configure(config2); + } + + /** + * Parses a line of an NT FTP server file listing and converts it into a + * usable format in the form of an FTPFile instance. If the + * file listing line doesn't describe a file, null is + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + FTPFile f = new FTPFile(); + f.setRawListing(entry); + + if (matches(entry)) { + String datestr = group(1) + " " + group(2); + String dirString = group(3); + String size = group(4); + String name = group(5); + try { + f.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // parsing fails, try the other date format + try { + f.setTimestamp(timestampParser.parseTimestamp(datestr)); + } catch (ParseException e2) { + // intentionally do nothing + } + } + + if (null == name || name.equals(".") || name.equals("..")) { + return (null); + } + f.setName(name); + + if ("".equals(dirString)) { + f.setType(FTPFile.DIRECTORY_TYPE); + f.setSize(0); + } else { + f.setType(FTPFile.FILE_TYPE); + if (null != size) { + f.setSize(Long.parseLong(size)); + } + } + return (f); + } + return null; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override public FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT, null); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java new file mode 100644 index 00000000..7585ebbe --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; + +/** + * Implementation of FTPFileEntryParser and FTPFileListParser for Netware Systems. Note that some of + * the proprietary + * extensions for Novell-specific operations are not supported. See + * + * http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html + * for more details. + * + * @version $Id: NetwareFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + * @since 1.5 + */ +public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + /** + * Default date format is e.g. Feb 22 2006 + */ + private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy"; + + /** + * Default recent date format is e.g. Feb 22 17:32 + */ + private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm"; + + /** + * this is the regular expression used by this parser. + * Example: d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2 + */ + private static final String REGEX = "(d|-){1}\\s+" // Directory/file flag + + "\\[([-A-Z]+)\\]\\s+" // Attributes RWCEAFMS or - + + "(\\S+)\\s+" + "(\\d+)\\s+" // Owner and size + + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format + + "\\s+(.*)"; // Filename (incl. spaces) + + /** + * The default constructor for a NetwareFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public NetwareFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of an NetwareFTPEntryParser object + * with something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public NetwareFTPEntryParser(FTPClientConfig config) { + super(REGEX); + configure(config); + } + + /** + * Parses a line of an NetwareFTP server file listing and converts it into a + * usable format in the form of an FTPFile instance. If the + * file listing line doesn't describe a file, null is + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + *

+ * Netware file permissions are in the following format: RWCEAFMS, and are explained as follows: + *

    + *
  • S - Supervisor; All rights. + *
  • R - Read; Right to open and read or execute. + *
  • W - Write; Right to open and modify. + *
  • C - Create; Right to create; when assigned to a file, allows a deleted file to be + * recovered. + *
  • E - Erase; Right to delete. + *
  • M - Modify; Right to rename a file and to change attributes. + *
  • F - File Scan; Right to see directory or file listings. + *
  • A - Access Control; Right to modify trustee assignments and the Inherited Rights + * Mask. + *
+ * + * See + * + * here + * for more details + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + + FTPFile f = new FTPFile(); + if (matches(entry)) { + String dirString = group(1); + String attrib = group(2); + String user = group(3); + String size = group(4); + String datestr = group(5); + String name = group(9); + + try { + f.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + //is it a DIR or a file + if (dirString.trim().equals("d")) { + f.setType(FTPFile.DIRECTORY_TYPE); + } else // Should be "-" + { + f.setType(FTPFile.FILE_TYPE); + } + + f.setUser(user); + + //set the name + f.setName(name.trim()); + + //set the size + f.setSize(Long.parseLong(size.trim())); + + // Now set the permissions (or at least a subset thereof - full permissions would probably require + // subclassing FTPFile and adding extra metainformation there) + if (attrib.indexOf("R") != -1) { + f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); + } + if (attrib.indexOf("W") != -1) { + f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); + } + + return (f); + } + return null; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_NETWARE, DEFAULT_DATE_FORMAT, + DEFAULT_RECENT_DATE_FORMAT); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java new file mode 100644 index 00000000..fc2413e9 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; + +/** + * Implementation of FTPFileEntryParser and FTPFileListParser for OS2 Systems. + * + * @version $Id: OS2FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl + +{ + + private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy HH:mm"; //11-09-01 12:30 + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = "\\s*([0-9]+)\\s*" + + "(\\s+|[A-Z]+)\\s*" + + "(DIR|\\s+)\\s*" + + "(\\S+)\\s+(\\S+)\\s+" /* date stuff */ + + "(\\S.*)"; + + /** + * The default constructor for a OS2FTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public OS2FTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of an OS2FTPEntryParser object + * with something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public OS2FTPEntryParser(FTPClientConfig config) { + super(REGEX); + configure(config); + } + + /** + * Parses a line of an OS2 FTP server file listing and converts it into a + * usable format in the form of an FTPFile instance. If the + * file listing line doesn't describe a file, null is + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + + FTPFile f = new FTPFile(); + if (matches(entry)) { + String size = group(1); + String attrib = group(2); + String dirString = group(3); + String datestr = group(4) + " " + group(5); + String name = group(6); + try { + f.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + //is it a DIR or a file + if (dirString.trim().equals("DIR") || attrib.trim().equals("DIR")) { + f.setType(FTPFile.DIRECTORY_TYPE); + } else { + f.setType(FTPFile.FILE_TYPE); + } + + //set the name + f.setName(name.trim()); + + //set the size + f.setSize(Long.parseLong(size.trim())); + + return (f); + } + return null; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_OS2, DEFAULT_DATE_FORMAT, null); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java new file mode 100644 index 00000000..43198267 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java @@ -0,0 +1,399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import java.io.File; +import java.text.ParseException; + +/** + * @version $Id: OS400FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + *
+ *          Example *FILE/*MEM FTP entries, when the current
+ *          working directory is a file of file system QSYS:
+ *          ------------------------------------------------
+ *
+ *          $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file
+ *            250-NAMEFMT set to 1.
+ *            250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory.
+ *          $ dir
+ *            227 Entering Passive Mode (10,200,36,33,40,249).
+ *            125 List started.
+ *            QPGMR          135168 22.06.13 13:18:19 *FILE
+ *            QPGMR                                   *MEM       MKCMD.MBR
+ *            QPGMR                                   *MEM       RUCALLTST.MBR
+ *            QPGMR                                   *MEM       RUCMDHLP.MBR
+ *            QPGMR                                   *MEM       RUCRTTST.MBR
+ *            250 List completed.
+ *
+ *
+ *          Example *FILE entry of an OS/400 save file:
+ *          ---------------------------------------------------
+ *
+ *          $ cwd /qsys.lib/rpgunit.lib
+ *            250 "/QSYS.LIB/RPGUNIT.LIB" is current library.
+ *          $ dir rpgunit.file
+ *            227 Entering Passive Mode (10,200,36,33,188,106).
+ *            125 List started.
+ *            QPGMR        16347136 29.06.13 15:45:09 *FILE      RPGUNIT.SAVF
+ *            250 List completed.
+ *
+ *
+ *          Example *STMF/*DIR FTP entries, when the
+ *          current working directory is in file system "root":
+ *          ---------------------------------------------------
+ *
+ *          $ cwd /home/raddatz
+ *            250 "/home/raddatz" is current directory.
+ *          $ dir test*
+ *            227 Entering Passive Mode (10,200,36,33,200,189).
+ *            125 List started.
+ *            RADDATZ           200 21.05.11 12:31:18 *STMF      TEST_RG_02_CRLF.properties
+ *            RADDATZ           187 08.05.11 12:31:40 *STMF      TEST_RG_02_LF.properties
+ *            RADDATZ           187 08.05.11 12:31:52 *STMF      TEST_RG_02_CR.properties
+ *            RADDATZ          8192 04.07.13 09:04:14 *DIR       testDir1/
+ *            RADDATZ          8192 04.07.13 09:04:17 *DIR       testDir2/
+ *            250 List completed.
+ *
+ *
+ *          Example 1, using ANT to list specific members of a file:
+ *          --------------------------------------------------------
+ *
+ *               <echo/>
+ *               <echo>Listing members of a file:</echo>
+ *
+ *               <ftp action="list"
+ *                    server="${ftp.server}"
+ *                    userid="${ftp.user}"
+ *                    password="${ftp.password}"
+ *                    binary="false"
+ *                    verbose="true"
+ *                    remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
+ *                    systemTypeKey="OS/400"
+ *                    listing="ftp-listing.txt"
+ *                    >
+ *                   <fileset dir="./i5-downloads-file" casesensitive="false">
+ *                       <include name="run*.mbr" />
+ *                   </fileset>
+ *               </ftp>
+ *
+ *          Output:
+ *          -------
+ *
+ *            [echo] Listing members of a file:
+ *             [ftp] listing files
+ *             [ftp] listing RUN.MBR
+ *             [ftp] listing RUNNER.MBR
+ *             [ftp] listing RUNNERBND.MBR
+ *             [ftp] 3 files listed
+ *
+ *
+ *          Example 2, using ANT to list specific members of all files of a library:
+ *          ------------------------------------------------------------------------
+ *
+ *               <echo/>
+ *               <echo>Listing members of all files of a library:</echo>
+ *
+ *               <ftp action="list"
+ *                    server="${ftp.server}"
+ *                    userid="${ftp.user}"
+ *                    password="${ftp.password}"
+ *                    binary="false"
+ *                    verbose="true"
+ *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ *                    systemTypeKey="OS/400"
+ *                    listing="ftp-listing.txt"
+ *                    >
+ *                   <fileset dir="./i5-downloads-lib" casesensitive="false">
+ *                       <include name="**\run*.mbr" />
+ *                   </fileset>
+ *               </ftp>
+ *
+ *          Output:
+ *          -------
+ *
+ *            [echo] Listing members of all files of a library:
+ *             [ftp] listing files
+ *             [ftp] listing RPGUNIT1.FILE\RUN.MBR
+ *             [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR
+ *             [ftp] listing RPGUNITT1.FILE\RUNT.MBR
+ *             [ftp] listing RPGUNITY1.FILE\RUN.MBR
+ *             [ftp] listing RPGUNITY1.FILE\RUNNER.MBR
+ *             [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR
+ *             [ftp] 6 files listed
+ *
+ *
+ *          Example 3, using ANT to download specific members of a file:
+ *          ------------------------------------------------------------
+ *
+ *               <echo/>
+ *               <echo>Downloading members of a file:</echo>
+ *
+ *               <ftp action="get"
+ *                    server="${ftp.server}"
+ *                    userid="${ftp.user}"
+ *                    password="${ftp.password}"
+ *                    binary="false"
+ *                    verbose="true"
+ *                    remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
+ *                    systemTypeKey="OS/400"
+ *                    >
+ *                   <fileset dir="./i5-downloads-file" casesensitive="false">
+ *                       <include name="run*.mbr" />
+ *                   </fileset>
+ *               </ftp>
+ *
+ *          Output:
+ *          -------
+ *
+ *            [echo] Downloading members of a file:
+ *             [ftp] getting files
+ *             [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT -
+ *          FTP\i5-downloads-file\RUN.MBR
+ *             [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT -
+ *          FTP\i5-downloads-file\RUNNER.MBR
+ *             [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT -
+ *          FTP\i5-downloads-file\RUNNERBND.MBR
+ *             [ftp] 3 files retrieved
+ *
+ *
+ *          Example 4, using ANT to download specific members of all files of a library:
+ *          ----------------------------------------------------------------------------
+ *
+ *               <echo/>
+ *               <echo>Downloading members of all files of a library:</echo>
+ *
+ *               <ftp action="get"
+ *                    server="${ftp.server}"
+ *                    userid="${ftp.user}"
+ *                    password="${ftp.password}"
+ *                    binary="false"
+ *                    verbose="true"
+ *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ *                    systemTypeKey="OS/400"
+ *                    >
+ *                   <fileset dir="./i5-downloads-lib" casesensitive="false">
+ *                       <include name="**\run*.mbr" />
+ *                   </fileset>
+ *               </ftp>
+ *
+ *          Output:
+ *          -------
+ *
+ *            [echo] Downloading members of all files of a library:
+ *             [ftp] getting files
+ *             [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR
+ *             [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR
+ *             [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR
+ *             [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR
+ *             [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR
+ *             [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT -
+ *          FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR
+ *             [ftp] 6 files retrieved
+ *
+ *
+ *          Example 5, using ANT to download a save file of a library:
+ *          ----------------------------------------------------------
+ *
+ *               <ftp action="get"
+ *                    server="${ftp.server}"
+ *                    userid="${ftp.user}"
+ *                    password="${ftp.password}"
+ *                    binary="true"
+ *                    verbose="true"
+ *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
+ *                    systemTypeKey="OS/400"
+ *                    >
+ *                 <fileset dir="./i5-downloads-savf" casesensitive="false">
+ *                     <include name="RPGUNIT.SAVF" />
+ *                 </fileset>
+ *               </ftp>
+ *
+ *          Output:
+ *          -------
+ *            [echo] Downloading save file:
+ *             [ftp] getting files
+ *             [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF
+ *             [ftp] 1 files retrieved
+ *
+ *          
+ */ +public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + private static final String DEFAULT_DATE_FORMAT = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24 + + private static final String REGEX = "(\\S+)\\s+" // user + + "(?:(\\d+)\\s+)?" // size, empty for members + + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members + + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM + + "(?:(\\S+)\\s*)?"; // filename, missing, when CWD is a *FILE + + /** + * The default constructor for a OS400FTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public OS400FTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of an OS400FTPEntryParser object + * with something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public OS400FTPEntryParser(FTPClientConfig config) { + super(REGEX); + configure(config); + } + + @Override public FTPFile parseFTPEntry(String entry) { + + FTPFile file = new FTPFile(); + file.setRawListing(entry); + int type; + + if (matches(entry)) { + String usr = group(1); + String filesize = group(2); + String datestr = ""; + if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) { + datestr = group(3) + " " + group(4); + } + String typeStr = group(5); + String name = group(6); + + boolean mustScanForPathSeparator = true; + + try { + file.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + if (typeStr.equalsIgnoreCase("*STMF")) { + type = FTPFile.FILE_TYPE; + if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { + return null; + } + } else if (typeStr.equalsIgnoreCase("*DIR")) { + type = FTPFile.DIRECTORY_TYPE; + if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { + return null; + } + } else if (typeStr.equalsIgnoreCase("*FILE")) { + // File, defines the structure of the data (columns of a row) + // but the data is stored in one or more members. Typically a + // source file contains multiple members whereas it is + // recommended (but not enforced) to use one member per data + // file. + // Save files are a special type of files which are used + // to save objects, e.g. for backups. + if (name != null && name.toUpperCase().endsWith(".SAVF")) { + mustScanForPathSeparator = false; + type = FTPFile.FILE_TYPE; + } else { + return null; + } + } else if (typeStr.equalsIgnoreCase("*MEM")) { + mustScanForPathSeparator = false; + type = FTPFile.FILE_TYPE; + + if (isNullOrEmpty(name)) { + return null; + } + if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) { + return null; + } + + // Quick and dirty bug fix to make SelectorUtils work. + // Class SelectorUtils uses 'File.separator' to splitt + // a given path into pieces. But actually it had to + // use the separator of the FTP server, which is a forward + // slash in case of an AS/400. + name = name.replace('/', File.separatorChar); + } else { + type = FTPFile.UNKNOWN_TYPE; + } + + file.setType(type); + + file.setUser(usr); + + try { + file.setSize(Long.parseLong(filesize)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + + if (name.endsWith("/")) { + name = name.substring(0, name.length() - 1); + } + if (mustScanForPathSeparator) { + int pos = name.lastIndexOf('/'); + if (pos > -1) { + name = name.substring(pos + 1); + } + } + + file.setName(name); + + return file; + } + return null; + } + + /** + * @param string String value that is checked for null + * or empty. + * @return true for null or empty values, + * else false. + */ + private boolean isNullOrEmpty(String string) { + if (string == null || string.length() == 0) { + return true; + } + return false; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_OS400, DEFAULT_DATE_FORMAT, null); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java new file mode 100644 index 00000000..72576687 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +/** + * This class encapsulates all errors that may be thrown by + * the process of an FTPFileEntryParserFactory creating and + * instantiating an FTPFileEntryParser. + */ +public class ParserInitializationException extends RuntimeException { + + private static final long serialVersionUID = 5563335279583210658L; + + /** + * Constucts a ParserInitializationException with just a message + * + * @param message Exception message + */ + public ParserInitializationException(String message) { + super(message); + } + + /** + * Constucts a ParserInitializationException with a message + * and a root cause. + * + * @param message Exception message + * @param rootCause root cause throwable that caused + * this to be thrown + */ + public ParserInitializationException(String message, Throwable rootCause) { + super(message, rootCause); + } + + /** + * returns the root cause of this exception or null + * if no root cause was specified. + * + * @return the root cause of this exception being thrown + * @deprecated use {@link #getCause()} instead + */ + @Deprecated public Throwable getRootCause() { + return super.getCause(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java new file mode 100644 index 00000000..90100f89 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * This abstract class implements both the older FTPFileListParser and + * newer FTPFileEntryParser interfaces with default functionality. + * All the classes in the parser subpackage inherit from this. + * + * This is the base class for all regular expression based FTPFileEntryParser classes + */ +public abstract class RegexFTPFileEntryParserImpl extends FTPFileEntryParserImpl { + /** + * internal pattern the matcher tries to match, representing a file + * entry + */ + private Pattern pattern = null; + + /** + * internal match result used by the parser + */ + private MatchResult result = null; + + /** + * Internal PatternMatcher object used by the parser. It has protected + * scope in case subclasses want to make use of it for their own purposes. + */ + protected Matcher _matcher_ = null; + + /** + * The constructor for a RegexFTPFileEntryParserImpl object. + * The expression is compiled with flags = 0. + * + * @param regex The regular expression with which this object is + * initialized. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen in + * normal conditions. It it is seen, this is a sign that a subclass has + * been created with a bad regular expression. Since the parser must be + * created before use, this means that any bad parser subclasses created + * from this will bomb very quickly, leading to easy detection. + */ + + public RegexFTPFileEntryParserImpl(String regex) { + super(); + compileRegex(regex, 0); + } + + /** + * The constructor for a RegexFTPFileEntryParserImpl object. + * + * @param regex The regular expression with which this object is + * initialized. + * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen in + * normal conditions. It it is seen, this is a sign that a subclass has + * been created with a bad regular expression. Since the parser must be + * created before use, this means that any bad parser subclasses created + * from this will bomb very quickly, leading to easy detection. + * @since 3.4 + */ + public RegexFTPFileEntryParserImpl(String regex, final int flags) { + super(); + compileRegex(regex, flags); + } + + /** + * Convenience method delegates to the internal MatchResult's matches() + * method. + * + * @param s the String to be matched + * @return true if s matches this object's regular expression. + */ + + public boolean matches(String s) { + this.result = null; + _matcher_ = pattern.matcher(s); + if (_matcher_.matches()) { + this.result = _matcher_.toMatchResult(); + } + return null != this.result; + } + + /** + * Convenience method + * + * @return the number of groups() in the internal MatchResult. + */ + + public int getGroupCnt() { + if (this.result == null) { + return 0; + } + return this.result.groupCount(); + } + + /** + * Convenience method delegates to the internal MatchResult's group() + * method. + * + * @param matchnum match group number to be retrieved + * @return the content of the matchnum'th group of the internal + * match or null if this method is called without a match having + * been made. + */ + public String group(int matchnum) { + if (this.result == null) { + return null; + } + return this.result.group(matchnum); + } + + /** + * For debugging purposes - returns a string shows each match group by + * number. + * + * @return a string shows each match group by number. + */ + + public String getGroupsAsString() { + StringBuilder b = new StringBuilder(); + for (int i = 1; i <= this.result.groupCount(); i++) { + b.append(i) + .append(") ") + .append(this.result.group(i)) + .append(System.getProperty("line.separator")); + } + return b.toString(); + } + + /** + * Alter the current regular expression being utilised for entry parsing + * and create a new {@link Pattern} instance. + * + * @param regex The new regular expression + * @return true + * @throws IllegalArgumentException if the regex cannot be compiled + * @since 2.0 + */ + public boolean setRegex(final String regex) { + compileRegex(regex, 0); + return true; + } + + /** + * Alter the current regular expression being utilised for entry parsing + * and create a new {@link Pattern} instance. + * + * @param regex The new regular expression + * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. + * @return true + * @throws IllegalArgumentException if the regex cannot be compiled + * @since 3.4 + */ + public boolean setRegex(final String regex, final int flags) { + compileRegex(regex, flags); + return true; + } + + /** + * Compile the regex and store the {@link Pattern}. + * + * This is an internal method to do the work so the constructor does not + * have to call an overrideable method. + * + * @param regex the expression to compile + * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. + * @throws IllegalArgumentException if the regex cannot be compiled + */ + private void compileRegex(final String regex, final int flags) { + try { + pattern = Pattern.compile(regex, flags); + } catch (PatternSyntaxException pse) { + throw new IllegalArgumentException("Unparseable regex supplied: " + regex); + } + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java new file mode 100644 index 00000000..5e35f308 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.text.ParseException; +import java.util.List; +import java.util.ListIterator; + +/** + * Implementation FTPFileEntryParser and FTPFileListParser for standard + * Unix Systems. + * + * This class is based on the logic of Daniel Savarese's + * DefaultFTPListParser, but adapted to use regular expressions and to fit the + * new FTPFileEntryParser interface. + * + * @version $Id: UnixFTPEntryParser.java 1781925 2017-02-06 16:43:40Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; //Nov 9 2001 + + static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; //Nov 9 20:06 + + static final String NUMERIC_DATE_FORMAT = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06 + + // Suffixes used in Japanese listings after the numeric values + private static final String JA_MONTH = "\u6708"; + private static final String JA_DAY = "\u65e5"; + private static final String JA_YEAR = "\u5e74"; + + private static final String DEFAULT_DATE_FORMAT_JA = + "M'" + JA_MONTH + "' d'" + JA_DAY + "' yyyy'" + JA_YEAR + "'"; //6月 3日 2003年 + + private static final String DEFAULT_RECENT_DATE_FORMAT_JA = + "M'" + JA_MONTH + "' d'" + JA_DAY + "' HH:mm"; //8月 17日 20:10 + + /** + * Some Linux distributions are now shipping an FTP server which formats + * file listing dates in an all-numeric format: + * "yyyy-MM-dd HH:mm. + * This is a very welcome development, and hopefully it will soon become + * the standard. However, since it is so new, for now, and possibly + * forever, we merely accomodate it, but do not make it the default. + *

+ * For now end users may specify this format only via + * UnixFTPEntryParser(FTPClientConfig). + * Steve Cohen - 2005-04-17 + */ + public static final FTPClientConfig NUMERIC_DATE_CONFIG = + new FTPClientConfig(FTPClientConfig.SYST_UNIX, NUMERIC_DATE_FORMAT, null); + + /** + * this is the regular expression used by this parser. + * + * Permissions: + * r the file is readable + * w the file is writable + * x the file is executable + * - the indicated permission is not granted + * L mandatory locking occurs during access (the set-group-ID bit is + * on and the group execution bit is off) + * s the set-user-ID or set-group-ID bit is on, and the corresponding + * user or group execution bit is also on + * S undefined bit-state (the set-user-ID bit is on and the user + * execution bit is off) + * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and + * execution is on + * T the 1000 bit is turned on, and execution is off (undefined bit- + * state) + * e z/OS external link bit + * Final letter may be appended: + * + file has extended security attributes (e.g. ACL) + * Note: local listings on MacOSX also use '@'; + * this is not allowed for here as does not appear to be shown by FTP servers + * {@code @} file has extended attributes + */ + private static final String REGEX = "([bcdelfmpSs-])" // file type + + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions + + + "\\s*" // separator TODO why allow it to be omitted?? + + + "(\\d+)" // link count + + + "\\s+" // separator + + + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) + + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) + + "(\\d+(?:,\\s*\\d+)?)" // size or n,m + + + "\\s+" // separator + + /* + * numeric or standard format date: + * yyyy-mm-dd (expecting hh:mm to follow) + * MMM [d]d + * [d]d MMM + * N.B. use non-space for MMM to allow for languages such as German which use + * diacritics (e.g. umlaut) in some abbreviations. + * Japanese uses numeric day and month with suffixes to distinguish them + * [d]dXX [d]dZZ + */ + "(" + "(?:\\d+[-/]\\d+[-/]\\d+)" + // yyyy-mm-dd + "|(?:\\S{3}\\s+\\d{1,2})" + // MMM [d]d + "|(?:\\d{1,2}\\s+\\S{3})" + // [d]d MMM + "|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + ")" + ")" + + + "\\s+" // separator + + /* + year (for non-recent standard format) - yyyy + or time (for numeric or recent standard format) [h]h:mm + or Japanese year - yyyyXX + */ + "((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))" // (20) + + + "\\s" // separator + + + "(.*)"; // the rest (21) + + // if true, leading spaces are trimmed from file names + // this was the case for the original implementation + final boolean trimLeadingSpaces; // package protected for access from test code + + /** + * The default constructor for a UnixFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public UnixFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of a UnixFTPEntryParser object with + * something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public UnixFTPEntryParser(FTPClientConfig config) { + this(config, false); + } + + /** + * This constructor allows the creation of a UnixFTPEntryParser object with + * something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @param trimLeadingSpaces if {@code true}, trim leading spaces from file names + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 3.4 + */ + public UnixFTPEntryParser(FTPClientConfig config, boolean trimLeadingSpaces) { + super(REGEX); + configure(config); + this.trimLeadingSpaces = trimLeadingSpaces; + } + + /** + * Preparse the list to discard "total nnn" lines + */ + @Override public List preParse(List original) { + ListIterator iter = original.listIterator(); + while (iter.hasNext()) { + String entry = iter.next(); + if (entry.matches("^total \\d+$")) { // NET-389 + iter.remove(); + } + } + return original; + } + + /** + * Parses a line of a unix (standard) FTP server file listing and converts + * it into a usable format in the form of an FTPFile + * instance. If the file listing line doesn't describe a file, + * null is returned, otherwise a FTPFile + * instance representing the files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + FTPFile file = new FTPFile(); + file.setRawListing(entry); + int type; + boolean isDevice = false; + + if (matches(entry)) { + String typeStr = group(1); + String hardLinkCount = group(15); + String usr = group(16); + String grp = group(17); + String filesize = group(18); + String datestr = group(19) + " " + group(20); + String name = group(21); + if (trimLeadingSpaces) { + name = name.replaceFirst("^\\s+", ""); + } + + try { + if (group(19).contains(JA_MONTH)) { // special processing for Japanese format + FTPTimestampParserImpl jaParser = new FTPTimestampParserImpl(); + jaParser.configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT_JA, + DEFAULT_RECENT_DATE_FORMAT_JA)); + file.setTimestamp(jaParser.parseTimestamp(datestr)); + } else { + file.setTimestamp(super.parseTimestamp(datestr)); + } + } catch (ParseException e) { + // intentionally do nothing + } + + // A 'whiteout' file is an ARTIFICIAL entry in any of several types of + // 'translucent' filesystems, of which a 'union' filesystem is one. + + // bcdelfmpSs- + switch (typeStr.charAt(0)) { + case 'd': + type = FTPFile.DIRECTORY_TYPE; + break; + case 'e': // NET-39 => z/OS external link + type = FTPFile.SYMBOLIC_LINK_TYPE; + break; + case 'l': + type = FTPFile.SYMBOLIC_LINK_TYPE; + break; + case 'b': + case 'c': + isDevice = true; + type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented + break; + case 'f': + case '-': + type = FTPFile.FILE_TYPE; + break; + default: // e.g. ? and w = whiteout + type = FTPFile.UNKNOWN_TYPE; + } + + file.setType(type); + + int g = 4; + for (int access = 0; access < 3; access++, g += 4) { + // Use != '-' to avoid having to check for suid and sticky bits + file.setPermission(access, FTPFile.READ_PERMISSION, (!group(g).equals("-"))); + file.setPermission(access, FTPFile.WRITE_PERMISSION, (!group(g + 1).equals("-"))); + + String execPerm = group(g + 2); + if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) { + file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); + } else { + file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); + } + } + + if (!isDevice) { + try { + file.setHardLinkCount(Integer.parseInt(hardLinkCount)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + } + + file.setUser(usr); + file.setGroup(grp); + + try { + file.setSize(Long.parseLong(filesize)); + } catch (NumberFormatException e) { + // intentionally do nothing + } + + // oddball cases like symbolic links, file names + // with spaces in them. + if (type == FTPFile.SYMBOLIC_LINK_TYPE) { + + int end = name.indexOf(" -> "); + // Give up if no link indicator is present + if (end == -1) { + file.setName(name); + } else { + file.setName(name.substring(0, end)); + file.setLink(name.substring(end + 4)); + } + } else { + file.setName(name); + } + return file; + } + return null; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, + DEFAULT_RECENT_DATE_FORMAT); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java new file mode 100644 index 00000000..b2578cd0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import aria.apache.commons.net.ftp.FTPListParseEngine; +import java.io.BufferedReader; +import java.io.IOException; +import java.text.ParseException; +import java.util.StringTokenizer; + +/** + * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems. + * This is a sample of VMS LIST output + * + * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + *

+ * Note: VMSFTPEntryParser can only be instantiated through the + * DefaultFTPParserFactory by classname. It will not be chosen + * by the autodetection scheme. + * + *

+ * + * @version $Id: VMSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + * @see DefaultFTPFileEntryParserFactory + */ +public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { + + private static final String DEFAULT_DATE_FORMAT = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24 + + /** + * this is the regular expression used by this parser. + */ + private static final String REGEX = + "(.*?;[0-9]+)\\s*" //1 file and version + + "(\\d+)/\\d+\\s*" //2 size/allocated + + "(\\S+)\\s+(\\S+)\\s+" //3+4 date and time + + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" //5(6,7,8) owner + + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; + //9,10,11 Permissions (O,G,W) + // TODO - perhaps restrict permissions to [RWED]* ? + + /** + * Constructor for a VMSFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public VMSFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of a VMSFTPEntryParser object with + * something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public VMSFTPEntryParser(FTPClientConfig config) { + super(REGEX); + configure(config); + } + + /** + * Parses a line of a VMS FTP server file listing and converts it into a + * usable format in the form of an FTPFile instance. If the + * file listing line doesn't describe a file, null is + * returned, otherwise a FTPFile instance representing the + * files in the directory is returned. + * + * @param entry A line of text from the file listing + * @return An FTPFile instance corresponding to the supplied entry + */ + @Override public FTPFile parseFTPEntry(String entry) { + //one block in VMS equals 512 bytes + long longBlock = 512; + + if (matches(entry)) { + FTPFile f = new FTPFile(); + f.setRawListing(entry); + String name = group(1); + String size = group(2); + String datestr = group(3) + " " + group(4); + String owner = group(5); + String permissions[] = new String[3]; + permissions[0] = group(9); + permissions[1] = group(10); + permissions[2] = group(11); + try { + f.setTimestamp(super.parseTimestamp(datestr)); + } catch (ParseException e) { + // intentionally do nothing + } + + String grp; + String user; + StringTokenizer t = new StringTokenizer(owner, ","); + switch (t.countTokens()) { + case 1: + grp = null; + user = t.nextToken(); + break; + case 2: + grp = t.nextToken(); + user = t.nextToken(); + break; + default: + grp = null; + user = null; + } + + if (name.lastIndexOf(".DIR") != -1) { + f.setType(FTPFile.DIRECTORY_TYPE); + } else { + f.setType(FTPFile.FILE_TYPE); + } + //set FTPFile name + //Check also for versions to be returned or not + if (isVersioning()) { + f.setName(name); + } else { + name = name.substring(0, name.lastIndexOf(";")); + f.setName(name); + } + //size is retreived in blocks and needs to be put in bytes + //for us humans and added to the FTPFile array + long sizeInBytes = Long.parseLong(size) * longBlock; + f.setSize(sizeInBytes); + + f.setGroup(grp); + f.setUser(user); + //set group and owner + + //Set file permission. + //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain + //R (read) W (write) E (execute) D (delete) + + //iterate for OWNER GROUP WORLD permissions + for (int access = 0; access < 3; access++) { + String permission = permissions[access]; + + f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R') >= 0); + f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W') >= 0); + f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E') >= 0); + } + + return f; + } + return null; + } + + /** + * Reads the next entry using the supplied BufferedReader object up to + * whatever delemits one entry from the next. This parser cannot use + * the default implementation of simply calling BufferedReader.readLine(), + * because one entry may span multiple lines. + * + * @param reader The BufferedReader object from which entries are to be + * read. + * @return A string representing the next ftp entry or null if none found. + * @throws IOException thrown on any IO Error reading from the reader. + */ + @Override public String readNextEntry(BufferedReader reader) throws IOException { + String line = reader.readLine(); + StringBuilder entry = new StringBuilder(); + while (line != null) { + if (line.startsWith("Directory") || line.startsWith("Total")) { + line = reader.readLine(); + continue; + } + + entry.append(line); + if (line.trim().endsWith(")")) { + break; + } + line = reader.readLine(); + } + return (entry.length() == 0 ? null : entry.toString()); + } + + protected boolean isVersioning() { + return false; + } + + /** + * Defines a default configuration to be used when this class is + * instantiated without a {@link FTPClientConfig FTPClientConfig} + * parameter being specified. + * + * @return the default configuration for this parser. + */ + @Override protected FTPClientConfig getDefaultConfiguration() { + return new FTPClientConfig(FTPClientConfig.SYST_VMS, DEFAULT_DATE_FORMAT, null); + } + + // DEPRECATED METHODS - for API compatibility only - DO NOT USE + + /** + * DO NOT USE + * + * @param listStream the stream + * @return the array of files + * @throws IOException on error + * @deprecated (2.2) No other FTPFileEntryParser implementations have this method. + */ + @Deprecated public FTPFile[] parseFileList(java.io.InputStream listStream) throws IOException { + FTPListParseEngine engine = + new FTPListParseEngine(this); + engine.readServerList(listStream, null); + return engine.getFiles(); + } +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java new file mode 100644 index 00000000..948662a7 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.ftp.parser; + +import aria.apache.commons.net.ftp.FTPClientConfig; +import aria.apache.commons.net.ftp.FTPFileEntryParser; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Special implementation VMSFTPEntryParser with versioning turned on. + * This parser removes all duplicates and only leaves the version with the highest + * version number for each filename. + * + * This is a sample of VMS LIST output + * + * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] + * (RWED,RWED,RWED,RE)", + *

+ * + * @version $Id: VMSVersioningFTPEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ + * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) + */ +public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser { + + private final Pattern _preparse_pattern_; + private static final String PRE_PARSE_REGEX = "(.*?);([0-9]+)\\s*.*"; + + /** + * Constructor for a VMSFTPEntryParser object. + * + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + */ + public VMSVersioningFTPEntryParser() { + this(null); + } + + /** + * This constructor allows the creation of a VMSVersioningFTPEntryParser + * object with something other than the default configuration. + * + * @param config The {@link FTPClientConfig configuration} object used to + * configure this parser. + * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not + * be seen + * under normal conditions. It it is seen, this is a sign that + * REGEX is not a valid regular expression. + * @since 1.4 + */ + public VMSVersioningFTPEntryParser(FTPClientConfig config) { + super(); + configure(config); + try { + //_preparse_matcher_ = new Perl5Matcher(); + _preparse_pattern_ = Pattern.compile(PRE_PARSE_REGEX); + } catch (PatternSyntaxException pse) { + throw new IllegalArgumentException("Unparseable regex supplied: " + PRE_PARSE_REGEX); + } + } + + /** + * Implement hook provided for those implementers (such as + * VMSVersioningFTPEntryParser, and possibly others) which return + * multiple files with the same name to remove the duplicates .. + * + * @param original Original list + * @return Original list purged of duplicates + */ + @Override public List preParse(List original) { + HashMap existingEntries = new HashMap(); + ListIterator iter = original.listIterator(); + while (iter.hasNext()) { + String entry = iter.next().trim(); + MatchResult result = null; + Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); + if (_preparse_matcher_.matches()) { + result = _preparse_matcher_.toMatchResult(); + String name = result.group(1); + String version = result.group(2); + Integer nv = Integer.valueOf(version); + Integer existing = existingEntries.get(name); + if (null != existing) { + if (nv.intValue() < existing.intValue()) { + iter.remove(); // removes older version from original list. + continue; + } + } + existingEntries.put(name, nv); + } + } + // we've now removed all entries less than with less than the largest + // version number for each name that were listed after the largest. + // we now must remove those with smaller than the largest version number + // for each name that were found before the largest + while (iter.hasPrevious()) { + String entry = iter.previous().trim(); + MatchResult result = null; + Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); + if (_preparse_matcher_.matches()) { + result = _preparse_matcher_.toMatchResult(); + String name = result.group(1); + String version = result.group(2); + Integer nv = Integer.valueOf(version); + Integer existing = existingEntries.get(name); + if (null != existing) { + if (nv.intValue() < existing.intValue()) { + iter.remove(); // removes older version from original list. + } + } + } + } + return original; + } + + @Override protected boolean isVersioning() { + return true; + } +} + +/* Emacs configuration + * Local variables: ** + * mode: java ** + * c-basic-offset: 4 ** + * indent-tabs-mode: nil ** + * End: ** + */ diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java new file mode 100644 index 00000000..147da7ad --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * FTP file listing parser classes + */ +package aria.apache.commons.net.ftp.parser; \ No newline at end of file diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java new file mode 100644 index 00000000..593214d7 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/** + * CRLFLineReader implements a readLine() method that requires + * exactly CRLF to terminate an input line. + * This is required for IMAP, which allows bare CR and LF. + * + * @since 3.0 + */ +public final class CRLFLineReader extends BufferedReader { + private static final char LF = '\n'; + private static final char CR = '\r'; + + /** + * Creates a CRLFLineReader that wraps an existing Reader + * input source. + * + * @param reader The Reader input source. + */ + public CRLFLineReader(Reader reader) { + super(reader); + } + + /** + * Read a line of text. + * A line is considered to be terminated by carriage return followed immediately by a linefeed. + * This contrasts with BufferedReader which also allows other combinations. + * + * @since 3.0 + */ + @Override public String readLine() throws IOException { + StringBuilder sb = new StringBuilder(); + int intch; + boolean prevWasCR = false; + synchronized (lock) { // make thread-safe (hopefully!) + while ((intch = read()) != -1) { + if (prevWasCR && intch == LF) { + return sb.substring(0, sb.length() - 1); + } + if (intch == CR) { + prevWasCR = true; + } else { + prevWasCR = false; + } + sb.append((char) intch); + } + } + String string = sb.toString(); + if (string.length() == 0) { // immediate EOF + return null; + } + return string; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java new file mode 100644 index 00000000..0f67dda9 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.util.EventListener; + +import aria.apache.commons.net.util.ListenerList; + +/** + * The CopyStreamAdapter will relay CopyStreamEvents to a list of listeners + * when either of its bytesTransferred() methods are called. Its purpose + * is to facilitate the notification of the progress of a copy operation + * performed by one of the static copyStream() methods in + * org.apache.commons.io.Util to multiple listeners. The static + * copyStream() methods invoke the + * bytesTransfered(long, int) of a CopyStreamListener for performance + * reasons and also because multiple listeners cannot be registered given + * that the methods are static. + * + * @version $Id: CopyStreamAdapter.java 1741829 2016-05-01 00:24:44Z sebb $ + * @see CopyStreamEvent + * @see CopyStreamListener + * @see Util + */ +public class CopyStreamAdapter implements CopyStreamListener { + private final ListenerList internalListeners; + + /** + * Creates a new copyStreamAdapter. + */ + public CopyStreamAdapter() { + internalListeners = new ListenerList(); + } + + /** + * This method is invoked by a CopyStreamEvent source after copying + * a block of bytes from a stream. The CopyStreamEvent will contain + * the total number of bytes transferred so far and the number of bytes + * transferred in the last write. The CopyStreamAdapater will relay + * the event to all of its registered listeners, listing itself as the + * source of the event. + * + * @param event The CopyStreamEvent fired by the copying of a block of + * bytes. + */ + @Override public void bytesTransferred(CopyStreamEvent event) { + for (EventListener listener : internalListeners) { + ((CopyStreamListener) (listener)).bytesTransferred(event); + } + } + + /** + * This method is not part of the JavaBeans model and is used by the + * static methods in the org.apache.commons.io.Util class for efficiency. + * It is invoked after a block of bytes to inform the listener of the + * transfer. The CopyStreamAdapater will create a CopyStreamEvent + * from the arguments and relay the event to all of its registered + * listeners, listing itself as the source of the event. + * + * @param totalBytesTransferred The total number of bytes transferred + * so far by the copy operation. + * @param bytesTransferred The number of bytes copied by the most recent + * write. + * @param streamSize The number of bytes in the stream being copied. + * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if + * the size is unknown. + */ + @Override public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, + long streamSize) { + for (EventListener listener : internalListeners) { + ((CopyStreamListener) (listener)).bytesTransferred(totalBytesTransferred, bytesTransferred, + streamSize); + } + } + + /** + * Registers a CopyStreamListener to receive CopyStreamEvents. + * Although this method is not declared to be synchronized, it is + * implemented in a thread safe manner. + * + * @param listener The CopyStreamlistener to register. + */ + public void addCopyStreamListener(CopyStreamListener listener) { + internalListeners.addListener(listener); + } + + /** + * Unregisters a CopyStreamListener. Although this method is not + * synchronized, it is implemented in a thread safe manner. + * + * @param listener The CopyStreamlistener to unregister. + */ + public void removeCopyStreamListener(CopyStreamListener listener) { + internalListeners.removeListener(listener); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java new file mode 100644 index 00000000..f44d918e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.util.EventObject; + +/** + * A CopyStreamEvent is triggered after every write performed by a + * stream copying operation. The event stores the number of bytes + * transferred by the write triggering the event as well as the total + * number of bytes transferred so far by the copy operation. + * + * @version $Id: CopyStreamEvent.java 1652801 2015-01-18 17:10:05Z sebb $ + * @see CopyStreamListener + * @see CopyStreamAdapter + * @see Util + */ +public class CopyStreamEvent extends EventObject { + private static final long serialVersionUID = -964927635655051867L; + + /** + * Constant used to indicate the stream size is unknown. + */ + public static final long UNKNOWN_STREAM_SIZE = -1; + + private final int bytesTransferred; + private final long totalBytesTransferred; + private final long streamSize; + + /** + * Creates a new CopyStreamEvent instance. + * + * @param source The source of the event. + * @param totalBytesTransferred The total number of bytes transferred so + * far during a copy operation. + * @param bytesTransferred The number of bytes transferred during the + * write that triggered the CopyStreamEvent. + * @param streamSize The number of bytes in the stream being copied. + * This may be set to UNKNOWN_STREAM_SIZE if the + * size is unknown. + */ + public CopyStreamEvent(Object source, long totalBytesTransferred, int bytesTransferred, + long streamSize) { + super(source); + this.bytesTransferred = bytesTransferred; + this.totalBytesTransferred = totalBytesTransferred; + this.streamSize = streamSize; + } + + /** + * Returns the number of bytes transferred by the write that triggered + * the event. + * + * @return The number of bytes transferred by the write that triggered + * the vent. + */ + public int getBytesTransferred() { + return bytesTransferred; + } + + /** + * Returns the total number of bytes transferred so far by the copy + * operation. + * + * @return The total number of bytes transferred so far by the copy + * operation. + */ + public long getTotalBytesTransferred() { + return totalBytesTransferred; + } + + /** + * Returns the size of the stream being copied. + * This may be set to UNKNOWN_STREAM_SIZE if the + * size is unknown. + * + * @return The size of the stream being copied. + */ + public long getStreamSize() { + return streamSize; + } + + /** + * @since 3.0 + */ + @Override public String toString() { + return getClass().getName() + + "[source=" + + source + + ", total=" + + totalBytesTransferred + + ", bytes=" + + bytesTransferred + + ", size=" + + streamSize + + "]"; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamException.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamException.java new file mode 100644 index 00000000..4314967c --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamException.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.IOException; + +/** + * The CopyStreamException class is thrown by the org.apache.commons.io.Util + * copyStream() methods. It stores the number of bytes confirmed to + * have been transferred before an I/O error as well as the IOException + * responsible for the failure of a copy operation. + * + * @version $Id: CopyStreamException.java 1299238 2012-03-10 17:12:28Z sebb $ + * @see Util + */ +public class CopyStreamException extends IOException { + private static final long serialVersionUID = -2602899129433221532L; + + private final long totalBytesTransferred; + + /** + * Creates a new CopyStreamException instance. + * + * @param message A message describing the error. + * @param bytesTransferred The total number of bytes transferred before + * an exception was thrown in a copy operation. + * @param exception The IOException thrown during a copy operation. + */ + public CopyStreamException(String message, long bytesTransferred, IOException exception) { + super(message); + initCause(exception); // merge this into super() call once we need 1.6+ + totalBytesTransferred = bytesTransferred; + } + + /** + * Returns the total number of bytes confirmed to have + * been transferred by a failed copy operation. + * + * @return The total number of bytes confirmed to have + * been transferred by a failed copy operation. + */ + public long getTotalBytesTransferred() { + return totalBytesTransferred; + } + + /** + * Returns the IOException responsible for the failure of a copy operation. + * + * @return The IOException responsible for the failure of a copy operation. + */ + public IOException getIOException() { + return (IOException) getCause(); // cast is OK because it was initialised with an IOException + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java new file mode 100644 index 00000000..14670feb --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.util.EventListener; + +/** + * The CopyStreamListener class can accept CopyStreamEvents to keep track + * of the progress of a stream copying operation. However, it is currently + * not used that way within NetComponents for performance reasons. Rather + * the bytesTransferred(long, int) method is called directly rather than + * passing an event to bytesTransferred(CopyStreamEvent), saving the creation + * of a CopyStreamEvent instance. Also, the only place where + * CopyStreamListener is currently used within NetComponents is in the + * static methods of the uninstantiable org.apache.commons.io.Util class, which + * would preclude the use of addCopyStreamListener and + * removeCopyStreamListener methods. However, future additions may use the + * JavaBean event model, which is why the hooks have been included from the + * beginning. + * + * @version $Id: CopyStreamListener.java 1652801 2015-01-18 17:10:05Z sebb $ + * @see CopyStreamEvent + * @see CopyStreamAdapter + * @see Util + */ +public interface CopyStreamListener extends EventListener { + /** + * This method is invoked by a CopyStreamEvent source after copying + * a block of bytes from a stream. The CopyStreamEvent will contain + * the total number of bytes transferred so far and the number of bytes + * transferred in the last write. + * + * @param event The CopyStreamEvent fired by the copying of a block of + * bytes. + */ + public void bytesTransferred(CopyStreamEvent event); + + /** + * This method is not part of the JavaBeans model and is used by the + * static methods in the org.apache.commons.io.Util class for efficiency. + * It is invoked after a block of bytes to inform the listener of the + * transfer. + * + * @param totalBytesTransferred The total number of bytes transferred + * so far by the copy operation. + * @param bytesTransferred The number of bytes copied by the most recent + * write. + * @param streamSize The number of bytes in the stream being copied. + * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if + * the size is unknown. + */ + public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize); +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java new file mode 100644 index 00000000..e554a359 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/** + * DotTerminatedMessageReader is a class used to read messages from a + * server that are terminated by a single dot followed by a + * <CR><LF> + * sequence and with double dots appearing at the begining of lines which + * do not signal end of message yet start with a dot. Various Internet + * protocols such as NNTP and POP3 produce messages of this type. + *

+ * This class handles stripping of the duplicate period at the beginning + * of lines starting with a period, and ensures you cannot read past the end of the message. + *

+ * Note: versions since 3.0 extend BufferedReader rather than Reader, + * and no longer change the CRLF into the local EOL. Also only DOT CR LF + * acts as EOF. + * + * @version $Id: DotTerminatedMessageReader.java 1747119 2016-06-07 02:22:24Z ggregory $ + */ +public final class DotTerminatedMessageReader extends BufferedReader { + private static final char LF = '\n'; + private static final char CR = '\r'; + private static final int DOT = '.'; + + private boolean atBeginning; + private boolean eof; + private boolean seenCR; // was last character CR? + + /** + * Creates a DotTerminatedMessageReader that wraps an existing Reader + * input source. + * + * @param reader The Reader input source containing the message. + */ + public DotTerminatedMessageReader(Reader reader) { + super(reader); + // Assumes input is at start of message + atBeginning = true; + eof = false; + } + + /** + * Reads and returns the next character in the message. If the end of the + * message has been reached, returns -1. Note that a call to this method + * may result in multiple reads from the underlying input stream to decode + * the message properly (removing doubled dots and so on). All of + * this is transparent to the programmer and is only mentioned for + * completeness. + * + * @return The next character in the message. Returns -1 if the end of the + * message has been reached. + * @throws IOException If an error occurs while reading the underlying + * stream. + */ + @Override public int read() throws IOException { + synchronized (lock) { + if (eof) { + return -1; // Don't allow read past EOF + } + int chint = super.read(); + if (chint == -1) { // True EOF + eof = true; + return -1; + } + if (atBeginning) { + atBeginning = false; + if (chint == DOT) { // Have DOT + mark(2); // need to check for CR LF or DOT + chint = super.read(); + if (chint == -1) { // Should not happen + // new Throwable("Trailing DOT").printStackTrace(); + eof = true; + return DOT; // return the trailing DOT + } + if (chint == DOT) { // Have DOT DOT + // no need to reset as we want to lose the first DOT + return chint; // i.e. DOT + } + if (chint == CR) { // Have DOT CR + chint = super.read(); + if (chint == -1) { // Still only DOT CR - should not happen + //new Throwable("Trailing DOT CR").printStackTrace(); + reset(); // So CR is picked up next time + return DOT; // return the trailing DOT + } + if (chint == LF) { // DOT CR LF + atBeginning = true; + eof = true; + // Do we need to clear the mark somehow? + return -1; + } + } + // Should not happen - lone DOT at beginning + //new Throwable("Lone DOT followed by "+(char)chint).printStackTrace(); + reset(); + return DOT; + } // have DOT + } // atBeginning + + // Handle CRLF in normal flow + if (seenCR) { + seenCR = false; + if (chint == LF) { + atBeginning = true; + } + } + if (chint == CR) { + seenCR = true; + } + return chint; + } + } + + /** + * Reads the next characters from the message into an array and + * returns the number of characters read. Returns -1 if the end of the + * message has been reached. + * + * @param buffer The character array in which to store the characters. + * @return The number of characters read. Returns -1 if the + * end of the message has been reached. + * @throws IOException If an error occurs in reading the underlying + * stream. + */ + @Override public int read(char[] buffer) throws IOException { + return read(buffer, 0, buffer.length); + } + + /** + * Reads the next characters from the message into an array and + * returns the number of characters read. Returns -1 if the end of the + * message has been reached. The characters are stored in the array + * starting from the given offset and up to the length specified. + * + * @param buffer The character array in which to store the characters. + * @param offset The offset into the array at which to start storing + * characters. + * @param length The number of characters to read. + * @return The number of characters read. Returns -1 if the + * end of the message has been reached. + * @throws IOException If an error occurs in reading the underlying + * stream. + */ + @Override public int read(char[] buffer, int offset, int length) throws IOException { + if (length < 1) { + return 0; + } + int ch; + synchronized (lock) { + if ((ch = read()) == -1) { + return -1; + } + + int off = offset; + + do { + buffer[offset++] = (char) ch; + } while (--length > 0 && (ch = read()) != -1); + + return (offset - off); + } + } + + /** + * Closes the message for reading. This doesn't actually close the + * underlying stream. The underlying stream may still be used for + * communicating with the server and therefore is not closed. + *

+ * If the end of the message has not yet been reached, this method + * will read the remainder of the message until it reaches the end, + * so that the underlying stream may continue to be used properly + * for communicating with the server. If you do not fully read + * a message, you MUST close it, otherwise your program will likely + * hang or behave improperly. + * + * @throws IOException If an error occurs while reading the + * underlying stream. + */ + @Override public void close() throws IOException { + synchronized (lock) { + if (!eof) { + while (read() != -1) { + // read to EOF + } + } + eof = true; + atBeginning = false; + } + } + + /** + * Read a line of text. + * A line is considered to be terminated by carriage return followed immediately by a linefeed. + * This contrasts with BufferedReader which also allows other combinations. + * + * @since 3.0 + */ + @Override public String readLine() throws IOException { + StringBuilder sb = new StringBuilder(); + int intch; + synchronized (lock) { // make thread-safe (hopefully!) + while ((intch = read()) != -1) { + if (intch == LF && atBeginning) { + return sb.substring(0, sb.length() - 1); + } + sb.append((char) intch); + } + } + String string = sb.toString(); + if (string.length() == 0) { // immediate EOF + return null; + } + // Should not happen - EOF without CRLF + //new Throwable(string).printStackTrace(); + return string; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java new file mode 100644 index 00000000..637399bd --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.IOException; +import java.io.Writer; + +/*** + * DotTerminatedMessageWriter is a class used to write messages to a + * server that are terminated by a single dot followed by a + * <CR><LF> + * sequence and with double dots appearing at the begining of lines which + * do not signal end of message yet start with a dot. Various Internet + * protocols such as NNTP and POP3 produce messages of this type. + *

+ * This class handles the doubling of line-starting periods, + * converts single linefeeds to NETASCII newlines, and on closing + * will send the final message terminator dot and NETASCII newline + * sequence. + * + * + ***/ + +public final class DotTerminatedMessageWriter extends Writer { + private static final int __NOTHING_SPECIAL_STATE = 0; + private static final int __LAST_WAS_CR_STATE = 1; + private static final int __LAST_WAS_NL_STATE = 2; + + private int __state; + private Writer __output; + + /*** + * Creates a DotTerminatedMessageWriter that wraps an existing Writer + * output destination. + * + * @param output The Writer output destination to write the message. + ***/ + public DotTerminatedMessageWriter(Writer output) { + super(output); + __output = output; + __state = __NOTHING_SPECIAL_STATE; + } + + /*** + * Writes a character to the output. Note that a call to this method + * may result in multiple writes to the underling Writer in order to + * convert naked linefeeds to NETASCII line separators and to double + * line-leading periods. This is transparent to the programmer and + * is only mentioned for completeness. + * + * @param ch The character to write. + * @throws IOException If an error occurs while writing to the + * underlying output. + ***/ + @Override public void write(int ch) throws IOException { + synchronized (lock) { + switch (ch) { + case '\r': + __state = __LAST_WAS_CR_STATE; + __output.write('\r'); + return; + case '\n': + if (__state != __LAST_WAS_CR_STATE) { + __output.write('\r'); + } + __output.write('\n'); + __state = __LAST_WAS_NL_STATE; + return; + case '.': + // Double the dot at the beginning of a line + if (__state == __LAST_WAS_NL_STATE) { + __output.write('.'); + } + //$FALL-THROUGH$ + default: + __state = __NOTHING_SPECIAL_STATE; + __output.write(ch); + return; + } + } + } + + /*** + * Writes a number of characters from a character array to the output + * starting from a given offset. + * + * @param buffer The character array to write. + * @param offset The offset into the array at which to start copying data. + * @param length The number of characters to write. + * @throws IOException If an error occurs while writing to the underlying + * output. + ***/ + @Override public void write(char[] buffer, int offset, int length) throws IOException { + synchronized (lock) { + while (length-- > 0) { + write(buffer[offset++]); + } + } + } + + /*** + * Writes a character array to the output. + * + * @param buffer The character array to write. + * @throws IOException If an error occurs while writing to the underlying + * output. + ***/ + @Override public void write(char[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + + /*** + * Writes a String to the output. + * + * @param string The String to write. + * @throws IOException If an error occurs while writing to the underlying + * output. + ***/ + @Override public void write(String string) throws IOException { + write(string.toCharArray()); + } + + /*** + * Writes part of a String to the output starting from a given offset. + * + * @param string The String to write. + * @param offset The offset into the String at which to start copying data. + * @param length The number of characters to write. + * @throws IOException If an error occurs while writing to the underlying + * output. + ***/ + @Override public void write(String string, int offset, int length) throws IOException { + write(string.toCharArray(), offset, length); + } + + /*** + * Flushes the underlying output, writing all buffered output. + * + * @throws IOException If an error occurs while writing to the underlying + * output. + ***/ + @Override public void flush() throws IOException { + synchronized (lock) { + __output.flush(); + } + } + + /*** + * Flushes the underlying output, writing all buffered output, but doesn't + * actually close the underlying stream. The underlying stream may still + * be used for communicating with the server and therefore is not closed. + * + * @throws IOException If an error occurs while writing to the underlying + * output or closing the Writer. + ***/ + @Override public void close() throws IOException { + synchronized (lock) { + if (__output == null) { + return; + } + + if (__state == __LAST_WAS_CR_STATE) { + __output.write('\n'); + } else if (__state != __LAST_WAS_NL_STATE) { + __output.write("\r\n"); + } + + __output.write(".\r\n"); + + __output.flush(); + __output = null; + } + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java new file mode 100644 index 00000000..f8aeeded --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.io.UnsupportedEncodingException; + +/*** + * This class wraps an input stream, replacing all occurrences + * of <CR><LF> (carriage return followed by a linefeed), + * which is the NETASCII standard for representing a newline, with the + * local line separator representation. You would use this class to + * implement ASCII file transfers requiring conversion from NETASCII. + * + * + ***/ + +public final class FromNetASCIIInputStream extends PushbackInputStream { + static final boolean _noConversionRequired; + static final String _lineSeparator; + static final byte[] _lineSeparatorBytes; + + static { + _lineSeparator = System.getProperty("line.separator"); + _noConversionRequired = _lineSeparator.equals("\r\n"); + try { + _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!", e); + } + } + + private int __length = 0; + + /*** + * Returns true if the NetASCII line separator differs from the system + * line separator, false if they are the same. This method is useful + * to determine whether or not you need to instantiate a + * FromNetASCIIInputStream object. + * + * @return True if the NETASCII line separator differs from the local + * system line separator, false if they are the same. + ***/ + public static final boolean isConversionRequired() { + return !_noConversionRequired; + } + + /*** + * Creates a FromNetASCIIInputStream instance that wraps an existing + * InputStream. + * @param input the stream to wrap + ***/ + public FromNetASCIIInputStream(InputStream input) { + super(input, _lineSeparatorBytes.length + 1); + } + + private int __read() throws IOException { + int ch; + + ch = super.read(); + + if (ch == '\r') { + ch = super.read(); + if (ch == '\n') { + unread(_lineSeparatorBytes); + ch = super.read(); + // This is a kluge for read(byte[], ...) to read the right amount + --__length; + } else { + if (ch != -1) { + unread(ch); + } + return '\r'; + } + } + + return ch; + } + + /*** + * Reads and returns the next byte in the stream. If the end of the + * message has been reached, returns -1. Note that a call to this method + * may result in multiple reads from the underlying input stream in order + * to convert NETASCII line separators to the local line separator format. + * This is transparent to the programmer and is only mentioned for + * completeness. + * + * @return The next character in the stream. Returns -1 if the end of the + * stream has been reached. + * @throws IOException If an error occurs while reading the underlying + * stream. + ***/ + @Override public int read() throws IOException { + if (_noConversionRequired) { + return super.read(); + } + + return __read(); + } + + /*** + * Reads the next number of bytes from the stream into an array and + * returns the number of bytes read. Returns -1 if the end of the + * stream has been reached. + * + * @param buffer The byte array in which to store the data. + * @return The number of bytes read. Returns -1 if the + * end of the message has been reached. + * @throws IOException If an error occurs in reading the underlying + * stream. + ***/ + @Override public int read(byte buffer[]) throws IOException { + return read(buffer, 0, buffer.length); + } + + /*** + * Reads the next number of bytes from the stream into an array and returns + * the number of bytes read. Returns -1 if the end of the + * message has been reached. The characters are stored in the array + * starting from the given offset and up to the length specified. + * + * @param buffer The byte array in which to store the data. + * @param offset The offset into the array at which to start storing data. + * @param length The number of bytes to read. + * @return The number of bytes read. Returns -1 if the + * end of the stream has been reached. + * @throws IOException If an error occurs while reading the underlying + * stream. + ***/ + @Override public int read(byte buffer[], int offset, int length) throws IOException { + if (_noConversionRequired) { + return super.read(buffer, offset, length); + } + + if (length < 1) { + return 0; + } + + int ch, off; + + ch = available(); + + __length = (length > ch ? ch : length); + + // If nothing is available, block to read only one character + if (__length < 1) { + __length = 1; + } + + if ((ch = __read()) == -1) { + return -1; + } + + off = offset; + + do { + buffer[offset++] = (byte) ch; + } while (--__length > 0 && (ch = __read()) != -1); + + return (offset - off); + } + + // PushbackInputStream in JDK 1.1.3 returns the wrong thing + // TODO - can we delete this override now? + + /*** + * Returns the number of bytes that can be read without blocking EXCEPT + * when newline conversions have to be made somewhere within the + * available block of bytes. In other words, you really should not + * rely on the value returned by this method if you are trying to avoid + * blocking. + ***/ + @Override public int available() throws IOException { + if (in == null) { + throw new IOException("Stream closed"); + } + return (buf.length - pos) + in.available(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java new file mode 100644 index 00000000..85e6112d --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/*** + * This class wraps an output stream, replacing all occurrences + * of <CR><LF> (carriage return followed by a linefeed), + * which is the NETASCII standard for representing a newline, with the + * local line separator representation. You would use this class to + * implement ASCII file transfers requiring conversion from NETASCII. + *

+ * Because of the translation process, a call to flush() will + * not flush the last byte written if that byte was a carriage + * return. A call to {@link #close close() }, however, will + * flush the carriage return. + * + * + ***/ + +public final class FromNetASCIIOutputStream extends FilterOutputStream { + private boolean __lastWasCR; + + /*** + * Creates a FromNetASCIIOutputStream instance that wraps an existing + * OutputStream. + * + * @param output The OutputStream to wrap. + ***/ + public FromNetASCIIOutputStream(OutputStream output) { + super(output); + __lastWasCR = false; + } + + private void __write(int ch) throws IOException { + switch (ch) { + case '\r': + __lastWasCR = true; + // Don't write anything. We need to see if next one is linefeed + break; + case '\n': + if (__lastWasCR) { + out.write(FromNetASCIIInputStream._lineSeparatorBytes); + __lastWasCR = false; + break; + } + __lastWasCR = false; + out.write('\n'); + break; + default: + if (__lastWasCR) { + out.write('\r'); + __lastWasCR = false; + } + out.write(ch); + break; + } + } + + /*** + * Writes a byte to the stream. Note that a call to this method + * might not actually write a byte to the underlying stream until a + * subsequent character is written, from which it can be determined if + * a NETASCII line separator was encountered. + * This is transparent to the programmer and is only mentioned for + * completeness. + * + * @param ch The byte to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(int ch) throws IOException { + if (FromNetASCIIInputStream._noConversionRequired) { + out.write(ch); + return; + } + + __write(ch); + } + + /*** + * Writes a byte array to the stream. + * + * @param buffer The byte array to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(byte buffer[]) throws IOException { + write(buffer, 0, buffer.length); + } + + /*** + * Writes a number of bytes from a byte array to the stream starting from + * a given offset. + * + * @param buffer The byte array to write. + * @param offset The offset into the array at which to start copying data. + * @param length The number of bytes to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(byte buffer[], int offset, int length) + throws IOException { + if (FromNetASCIIInputStream._noConversionRequired) { + // FilterOutputStream method is very slow. + //super.write(buffer, offset, length); + out.write(buffer, offset, length); + return; + } + + while (length-- > 0) { + __write(buffer[offset++]); + } + } + + /*** + * Closes the stream, writing all pending data. + * + * @throws IOException If an error occurs while closing the stream. + ***/ + @Override public synchronized void close() throws IOException { + if (FromNetASCIIInputStream._noConversionRequired) { + super.close(); + return; + } + + if (__lastWasCR) { + out.write('\r'); + } + super.close(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketInputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketInputStream.java new file mode 100644 index 00000000..531e4029 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketInputStream.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; + +/*** + * This class wraps an input stream, storing a reference to its originating + * socket. When the stream is closed, it will also close the socket + * immediately afterward. This class is useful for situations where you + * are dealing with a stream originating from a socket, but do not have + * a reference to the socket, and want to make sure it closes when the + * stream closes. + * + * + * @see SocketOutputStream + ***/ + +public class SocketInputStream extends FilterInputStream { + private final Socket __socket; + + /*** + * Creates a SocketInputStream instance wrapping an input stream and + * storing a reference to a socket that should be closed on closing + * the stream. + * + * @param socket The socket to close on closing the stream. + * @param stream The input stream to wrap. + ***/ + public SocketInputStream(Socket socket, InputStream stream) { + super(stream); + __socket = socket; + } + + /*** + * Closes the stream and immediately afterward closes the referenced + * socket. + * + * @throws IOException If there is an error in closing the stream + * or socket. + ***/ + @Override public void close() throws IOException { + super.close(); + __socket.close(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java new file mode 100644 index 00000000..3393ded7 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; + +/*** + * This class wraps an output stream, storing a reference to its originating + * socket. When the stream is closed, it will also close the socket + * immediately afterward. This class is useful for situations where you + * are dealing with a stream originating from a socket, but do not have + * a reference to the socket, and want to make sure it closes when the + * stream closes. + * + * + * @see SocketInputStream + ***/ + +public class SocketOutputStream extends FilterOutputStream { + private final Socket __socket; + + /*** + * Creates a SocketOutputStream instance wrapping an output stream and + * storing a reference to a socket that should be closed on closing + * the stream. + * + * @param socket The socket to close on closing the stream. + * @param stream The input stream to wrap. + ***/ + public SocketOutputStream(Socket socket, OutputStream stream) { + super(stream); + __socket = socket; + } + + /*** + * Writes a number of bytes from a byte array to the stream starting from + * a given offset. This method bypasses the equivalent method in + * FilterOutputStream because the FilterOutputStream implementation is + * very inefficient. + * + * @param buffer The byte array to write. + * @param offset The offset into the array at which to start copying data. + * @param length The number of bytes to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public void write(byte buffer[], int offset, int length) throws IOException { + out.write(buffer, offset, length); + } + + /*** + * Closes the stream and immediately afterward closes the referenced + * socket. + * + * @throws IOException If there is an error in closing the stream + * or socket. + ***/ + @Override public void close() throws IOException { + super.close(); + __socket.close(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java new file mode 100644 index 00000000..e7ee8bc5 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/*** + * This class wraps an input stream, replacing all singly occurring + * <LF> (linefeed) characters with <CR><LF> (carriage return + * followed by linefeed), which is the NETASCII standard for representing + * a newline. + * You would use this class to implement ASCII file transfers requiring + * conversion to NETASCII. + * + * + ***/ + +public final class ToNetASCIIInputStream extends FilterInputStream { + private static final int __NOTHING_SPECIAL = 0; + private static final int __LAST_WAS_CR = 1; + private static final int __LAST_WAS_NL = 2; + private int __status; + + /*** + * Creates a ToNetASCIIInputStream instance that wraps an existing + * InputStream. + * + * @param input The InputStream to wrap. + ***/ + public ToNetASCIIInputStream(InputStream input) { + super(input); + __status = __NOTHING_SPECIAL; + } + + /*** + * Reads and returns the next byte in the stream. If the end of the + * message has been reached, returns -1. + * + * @return The next character in the stream. Returns -1 if the end of the + * stream has been reached. + * @throws IOException If an error occurs while reading the underlying + * stream. + ***/ + @Override public int read() throws IOException { + int ch; + + if (__status == __LAST_WAS_NL) { + __status = __NOTHING_SPECIAL; + return '\n'; + } + + ch = in.read(); + + switch (ch) { + case '\r': + __status = __LAST_WAS_CR; + return '\r'; + case '\n': + if (__status != __LAST_WAS_CR) { + __status = __LAST_WAS_NL; + return '\r'; + } + //$FALL-THROUGH$ + default: + __status = __NOTHING_SPECIAL; + return ch; + } + // statement not reached + //return ch; + } + + /*** + * Reads the next number of bytes from the stream into an array and + * returns the number of bytes read. Returns -1 if the end of the + * stream has been reached. + * + * @param buffer The byte array in which to store the data. + * @return The number of bytes read. Returns -1 if the + * end of the message has been reached. + * @throws IOException If an error occurs in reading the underlying + * stream. + ***/ + @Override public int read(byte buffer[]) throws IOException { + return read(buffer, 0, buffer.length); + } + + /*** + * Reads the next number of bytes from the stream into an array and returns + * the number of bytes read. Returns -1 if the end of the + * message has been reached. The characters are stored in the array + * starting from the given offset and up to the length specified. + * + * @param buffer The byte array in which to store the data. + * @param offset The offset into the array at which to start storing data. + * @param length The number of bytes to read. + * @return The number of bytes read. Returns -1 if the + * end of the stream has been reached. + * @throws IOException If an error occurs while reading the underlying + * stream. + ***/ + @Override public int read(byte buffer[], int offset, int length) throws IOException { + int ch, off; + + if (length < 1) { + return 0; + } + + ch = available(); + + if (length > ch) { + length = ch; + } + + // If nothing is available, block to read only one character + if (length < 1) { + length = 1; + } + + if ((ch = read()) == -1) { + return -1; + } + + off = offset; + + do { + buffer[offset++] = (byte) ch; + } while (--length > 0 && (ch = read()) != -1); + + return (offset - off); + } + + /*** Returns false. Mark is not supported. ***/ + @Override public boolean markSupported() { + return false; + } + + @Override public int available() throws IOException { + int result; + + result = in.available(); + + if (__status == __LAST_WAS_NL) { + return (result + 1); + } + + return result; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java new file mode 100644 index 00000000..d54a8758 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/*** + * This class wraps an output stream, replacing all singly occurring + * <LF> (linefeed) characters with <CR><LF> (carriage return + * followed by linefeed), which is the NETASCII standard for representing + * a newline. + * You would use this class to implement ASCII file transfers requiring + * conversion to NETASCII. + * + * + ***/ + +public final class ToNetASCIIOutputStream extends FilterOutputStream { + private boolean __lastWasCR; + + /*** + * Creates a ToNetASCIIOutputStream instance that wraps an existing + * OutputStream. + * + * @param output The OutputStream to wrap. + ***/ + public ToNetASCIIOutputStream(OutputStream output) { + super(output); + __lastWasCR = false; + } + + /*** + * Writes a byte to the stream. Note that a call to this method + * may result in multiple writes to the underlying input stream in order + * to convert naked newlines to NETASCII line separators. + * This is transparent to the programmer and is only mentioned for + * completeness. + * + * @param ch The byte to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(int ch) throws IOException { + switch (ch) { + case '\r': + __lastWasCR = true; + out.write('\r'); + return; + case '\n': + if (!__lastWasCR) { + out.write('\r'); + } + //$FALL-THROUGH$ + default: + __lastWasCR = false; + out.write(ch); + return; + } + } + + /*** + * Writes a byte array to the stream. + * + * @param buffer The byte array to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(byte buffer[]) throws IOException { + write(buffer, 0, buffer.length); + } + + /*** + * Writes a number of bytes from a byte array to the stream starting from + * a given offset. + * + * @param buffer The byte array to write. + * @param offset The offset into the array at which to start copying data. + * @param length The number of bytes to write. + * @throws IOException If an error occurs while writing to the underlying + * stream. + ***/ + @Override public synchronized void write(byte buffer[], int offset, int length) + throws IOException { + while (length-- > 0) { + write(buffer[offset++]); + } + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/Util.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/Util.java new file mode 100644 index 00000000..bdf74b76 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/Util.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.io; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.Socket; + +/*** + * The Util class cannot be instantiated and stores short static convenience + * methods that are often quite useful. + * + * + * @see CopyStreamException + * @see CopyStreamListener + * @see CopyStreamAdapter + ***/ + +public final class Util { + /** + * The default buffer size ({@value}) used by + * {@link #copyStream copyStream } and {@link #copyReader copyReader} + * and by the copyReader/copyStream methods if a zero or negative buffer size is supplied. + */ + public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; + + // Cannot be instantiated + private Util() { + } + + /*** + * Copies the contents of an InputStream to an OutputStream using a + * copy buffer of a given size and notifies the provided + * CopyStreamListener of the progress of the copy operation by calling + * its bytesTransferred(long, int) method after each write to the + * destination. If you wish to notify more than one listener you should + * use a CopyStreamAdapter as the listener and register the additional + * listeners with the CopyStreamAdapter. + *

+ * The contents of the InputStream are + * read until the end of the stream is reached, but neither the + * source nor the destination are closed. You must do this yourself + * outside of the method call. The number of bytes read/written is + * returned. + * + * @param source The source InputStream. + * @param dest The destination OutputStream. + * @param bufferSize The number of bytes to buffer during the copy. + * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. + * @param streamSize The number of bytes in the stream being copied. + * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. + * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} + * @param listener The CopyStreamListener to notify of progress. If + * this parameter is null, notification is not attempted. + * @param flush Whether to flush the output stream after every + * write. This is necessary for interactive sessions that rely on + * buffered streams. If you don't flush, the data will stay in + * the stream buffer. + * @return number of bytes read/written + * @throws CopyStreamException If an error occurs while reading from the + * source or writing to the destination. The CopyStreamException + * will contain the number of bytes confirmed to have been + * transferred before an + * IOException occurred, and it will also contain the IOException + * that caused the error. These values can be retrieved with + * the CopyStreamException getTotalBytesTransferred() and + * getIOException() methods. + ***/ + public static final long copyStream(InputStream source, OutputStream dest, int bufferSize, + long streamSize, CopyStreamListener listener, boolean flush) throws CopyStreamException { + int numBytes; + long total = 0; + byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; + + try { + while (!Thread.currentThread().isInterrupted() && (numBytes = source.read(buffer)) != -1) { + // Technically, some read(byte[]) methods may return 0 and we cannot + // accept that as an indication of EOF. + + if (numBytes == 0) { + int singleByte = source.read(); + if (singleByte < 0) { + break; + } + dest.write(singleByte); + if (flush) { + dest.flush(); + } + ++total; + if (listener != null) { + listener.bytesTransferred(total, 1, streamSize); + } + continue; + } + + dest.write(buffer, 0, numBytes); + if (flush) { + dest.flush(); + } + total += numBytes; + if (listener != null) { + listener.bytesTransferred(total, numBytes, streamSize); + } + } + } catch (IOException e) { + throw new CopyStreamException("IOException caught while copying.", total, e); + } + + return total; + } + + + /*** + * Copies the contents of an InputStream to an OutputStream using a + * copy buffer of a given size and notifies the provided + * CopyStreamListener of the progress of the copy operation by calling + * its bytesTransferred(long, int) method after each write to the + * destination. If you wish to notify more than one listener you should + * use a CopyStreamAdapter as the listener and register the additional + * listeners with the CopyStreamAdapter. + *

+ * The contents of the InputStream are + * read until the end of the stream is reached, but neither the + * source nor the destination are closed. You must do this yourself + * outside of the method call. The number of bytes read/written is + * returned. + * + * @param source The source InputStream. + * @param dest The destination OutputStream. + * @param bufferSize The number of bytes to buffer during the copy. + * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. + * @param streamSize The number of bytes in the stream being copied. + * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. + * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} + * @param listener The CopyStreamListener to notify of progress. If + * this parameter is null, notification is not attempted. + * @return number of bytes read/written + * @throws CopyStreamException If an error occurs while reading from the + * source or writing to the destination. The CopyStreamException + * will contain the number of bytes confirmed to have been + * transferred before an + * IOException occurred, and it will also contain the IOException + * that caused the error. These values can be retrieved with + * the CopyStreamException getTotalBytesTransferred() and + * getIOException() methods. + ***/ + public static final long copyStream(InputStream source, OutputStream dest, int bufferSize, + long streamSize, CopyStreamListener listener) throws CopyStreamException { + return copyStream(source, dest, bufferSize, streamSize, listener, true); + } + + /*** + * Copies the contents of an InputStream to an OutputStream using a + * copy buffer of a given size. The contents of the InputStream are + * read until the end of the stream is reached, but neither the + * source nor the destination are closed. You must do this yourself + * outside of the method call. The number of bytes read/written is + * returned. + * + * @param source The source InputStream. + * @param dest The destination OutputStream. + * @param bufferSize The number of bytes to buffer during the copy. + * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. + * @return The number of bytes read/written in the copy operation. + * @throws CopyStreamException If an error occurs while reading from the + * source or writing to the destination. The CopyStreamException + * will contain the number of bytes confirmed to have been + * transferred before an + * IOException occurred, and it will also contain the IOException + * that caused the error. These values can be retrieved with + * the CopyStreamException getTotalBytesTransferred() and + * getIOException() methods. + ***/ + public static final long copyStream(InputStream source, OutputStream dest, int bufferSize) + throws CopyStreamException { + return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); + } + + /*** + * Same as copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); + * @param source where to copy from + * @param dest where to copy to + * @return number of bytes copied + * @throws CopyStreamException on error + ***/ + public static final long copyStream(InputStream source, OutputStream dest) + throws CopyStreamException { + return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); + } + + /*** + * Copies the contents of a Reader to a Writer using a + * copy buffer of a given size and notifies the provided + * CopyStreamListener of the progress of the copy operation by calling + * its bytesTransferred(long, int) method after each write to the + * destination. If you wish to notify more than one listener you should + * use a CopyStreamAdapter as the listener and register the additional + * listeners with the CopyStreamAdapter. + *

+ * The contents of the Reader are + * read until its end is reached, but neither the source nor the + * destination are closed. You must do this yourself outside of the + * method call. The number of characters read/written is returned. + * + * @param source The source Reader. + * @param dest The destination writer. + * @param bufferSize The number of characters to buffer during the copy. + * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. + * @param streamSize The number of characters in the stream being copied. + * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. + * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} + * @param listener The CopyStreamListener to notify of progress. If + * this parameter is null, notification is not attempted. + * @return The number of characters read/written in the copy operation. + * @throws CopyStreamException If an error occurs while reading from the + * source or writing to the destination. The CopyStreamException + * will contain the number of bytes confirmed to have been + * transferred before an + * IOException occurred, and it will also contain the IOException + * that caused the error. These values can be retrieved with + * the CopyStreamException getTotalBytesTransferred() and + * getIOException() methods. + ***/ + public static final long copyReader(Reader source, Writer dest, int bufferSize, long streamSize, + CopyStreamListener listener) throws CopyStreamException { + int numChars; + long total = 0; + char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; + + try { + while ((numChars = source.read(buffer)) != -1) { + // Technically, some read(char[]) methods may return 0 and we cannot + // accept that as an indication of EOF. + if (numChars == 0) { + int singleChar = source.read(); + if (singleChar < 0) { + break; + } + dest.write(singleChar); + dest.flush(); + ++total; + if (listener != null) { + listener.bytesTransferred(total, 1, streamSize); + } + continue; + } + + dest.write(buffer, 0, numChars); + dest.flush(); + total += numChars; + if (listener != null) { + listener.bytesTransferred(total, numChars, streamSize); + } + } + } catch (IOException e) { + throw new CopyStreamException("IOException caught while copying.", total, e); + } + + return total; + } + + /*** + * Copies the contents of a Reader to a Writer using a + * copy buffer of a given size. The contents of the Reader are + * read until its end is reached, but neither the source nor the + * destination are closed. You must do this yourself outside of the + * method call. The number of characters read/written is returned. + * + * @param source The source Reader. + * @param dest The destination writer. + * @param bufferSize The number of characters to buffer during the copy. + * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. + * @return The number of characters read/written in the copy operation. + * @throws CopyStreamException If an error occurs while reading from the + * source or writing to the destination. The CopyStreamException + * will contain the number of bytes confirmed to have been + * transferred before an + * IOException occurred, and it will also contain the IOException + * that caused the error. These values can be retrieved with + * the CopyStreamException getTotalBytesTransferred() and + * getIOException() methods. + ***/ + public static final long copyReader(Reader source, Writer dest, int bufferSize) + throws CopyStreamException { + return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); + } + + /*** + * Same as copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); + * @param source where to copy from + * @param dest where to copy to + * @return number of bytes copied + * @throws CopyStreamException on error + ***/ + public static final long copyReader(Reader source, Writer dest) throws CopyStreamException { + return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); + } + + /** + * Closes the object quietly, catching rather than throwing IOException. + * Intended for use from finally blocks. + * + * @param closeable the object to close, may be {@code null} + * @since 3.0 + */ + public static void closeQuietly(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + // Ignored + } + } + } + + /** + * Closes the socket quietly, catching rather than throwing IOException. + * Intended for use from finally blocks. + * + * @param socket the socket to close, may be {@code null} + * @since 3.0 + */ + public static void closeQuietly(Socket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // Ignored + } + } + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/io/package-info.java b/FtpComponent/src/main/java/aria/apache/commons/net/io/package-info.java new file mode 100644 index 00000000..06207d12 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/io/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Utility classes for IO support. + */ +package aria.apache.commons.net.io; \ No newline at end of file diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/Base64.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/Base64.java new file mode 100644 index 00000000..a03150e0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/Base64.java @@ -0,0 +1,1056 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.util; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

+ * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 + * Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and + * Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various constructors: + *

    + *
  • URL-safe mode: Default off.
  • + *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up + * being multiples of + * 4 in the encoded data. + *
  • Line separator: Default is CRLF ("\r\n")
  • + *
+ *

+ * Since this class operates directly on byte streams, and not character streams, it is hard-coded + * to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, + * Windows-1252, UTF-8, etc). + *

+ * + * @version $Id: Base64.java 1697293 2015-08-24 01:01:00Z sebb $ + * @see RFC 2045 + * @since 2.2 + */ +public class Base64 { + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, + * including any + * equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + static final int CHUNK_SIZE = 76; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + private static final byte[] CHUNK_SEPARATOR = { '\r', '\n' }; + + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** + * This array is a lookup table that translates 6-bit positive integer index values into their + * "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / + * changed to - and _ to make the encoded Base64 results more URL-SAFE. + * This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '-', '_' + }; + + /** + * Byte used to pad output. + */ + private static final byte PAD = '='; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 + * Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in + * the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder + * seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what + * to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, + -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, + 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51 + }; + + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + /** Mask used to extract 8 bits, used in decoding base64 bytes */ + private static final int MASK_8BITS = 0xff; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static + * because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so + * we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + /** + * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking + * of the base64 + * encoded data. + */ + private final int lineLength; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs + * resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs + * resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Buffer for streaming. + */ + private byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + private int pos; + + /** + * Position where next character should be read from the buffer. + */ + private int readPos; + + /** + * Variable tracks how many characters have been written to the current line. Only used when + * encoding. We use it to + * make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + private int currentLinePos; + + /** + * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when + * decoding. This variable + * helps track that. + */ + private int modulus; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 + * object becomes useless, + * and must be thrown away. + */ + private boolean eof; + + /** + * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store + * and extract the + * base64 encoding or decoding from this variable. + */ + private int x; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64() { + this(false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe if true, URL-safe encoding is used. In most cases this should be + * set to + * false. + * @since 1.4 + */ + public Base64(boolean urlSafe) { + this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator is CRLF, and the + * encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in + * the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given length (rounded down + * to nearest multiple of 4). + * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored + * when decoding. + * @since 1.4 + */ + public Base64(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding + * table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in + * the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given length (rounded down + * to nearest multiple of 4). + * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored + * when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 + * characters. + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding + * table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in + * the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength Each line of encoded data will be at most of the given length (rounded down + * to nearest multiple of 4). + * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored + * when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is + * only applied to encode + * operations. Decoding seamlessly handles both modes. + * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. + * That's not going to work! + * @since 1.4 + */ + public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { + if (lineSeparator == null) { + lineLength = 0; // disable chunk-separating + lineSeparator = EMPTY_BYTE_ARRAY; // this just gets ignored + } + this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + if (lineLength > 0) { + this.encodeSize = 4 + lineSeparator.length; + } else { + this.encodeSize = 4; + } + this.decodeSize = this.encodeSize - 1; + if (containsBase64Byte(lineSeparator)) { + String sep = newStringUtf8(lineSeparator); + throw new IllegalArgumentException( + "lineSeperator must not contain base64 characters: [" + sep + "]"); + } + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + * Returns true if this Base64 object has buffered data for reading. + * + * @return true if there is Base64 object still available for reading. + */ + boolean hasData() { + return this.buffer != null; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @return The amount of buffered data available for reading. + */ + int avail() { + return buffer != null ? pos - readPos : 0; + } + + /** Doubles our buffer. */ + private void resizeBuffer() { + if (buffer == null) { + buffer = new byte[DEFAULT_BUFFER_SIZE]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(buffer, 0, b, 0, buffer.length); + buffer = b; + } + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a + * maximum of bAvail + * bytes. Returns how many bytes were actually extracted. + * + * @param b byte[] array to extract the buffered data into. + * @param bPos position in byte[] array to start extraction at. + * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are + * available). + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + int readResults(byte[] b, int bPos, int bAvail) { + if (buffer != null) { + int len = Math.min(avail(), bAvail); + if (buffer != b) { + System.arraycopy(buffer, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buffer = null; + } + } else { + // Re-using the original consumer's output array is only + // allowed for one round. + buffer = null; + } + return len; + } + return eof ? -1 : 0; + } + + /** + * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the + * consumer's output + * array for one round (if the consumer calls this method first) instead of starting our own + * buffer. + * + * @param out byte[] array to buffer directly to. + * @param outPos Position to start buffering into. + * @param outAvail Amount of bytes available for direct buffering. + */ + void setInitialBuffer(byte[] out, int outPos, int outAvail) { + // We can re-use consumer's original output array under + // special circumstances, saving on some System.arraycopy(). + if (out != null && out.length == outAvail) { + buffer = out; + pos = outPos; + readPos = outPos; + } + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least + * twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been + * reached, so flush last + * remaining bytes (if not multiple of 3). + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (buffer == null || buffer.length - pos < encodeSize) { + resizeBuffer(); + } + switch (modulus) { + case 1: + buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + buffer[pos++] = PAD; + } + break; + + case 2: + buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[pos++] = PAD; + } + break; + default: + break; // other values ignored + } + if (lineLength > 0 && pos > 0) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + if (buffer == null || buffer.length - pos < encodeSize) { + resizeBuffer(); + } + modulus = (++modulus) % 3; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + x = (x << 8) + b; + if (0 == modulus) { + buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS]; + buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS]; + buffer[pos++] = encodeTable[x & MASK_6BITS]; + currentLinePos += 4; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at + * least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been + * reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, + * since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the + * garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of ascii data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + if (buffer == null || buffer.length - pos < decodeSize) { + resizeBuffer(); + } + byte b = in[inPos++]; + if (b == PAD) { + // We're done. + eof = true; + break; + } else { + if (b >= 0 && b < DECODE_TABLE.length) { + int result = DECODE_TABLE[b]; + if (result >= 0) { + modulus = (++modulus) % 4; + x = (x << 6) + result; + if (modulus == 0) { + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); + buffer[pos++] = (byte) (x & MASK_8BITS); + } + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (eof && modulus != 0) { + x = x << 6; + switch (modulus) { + case 2: + x = x << 6; + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + break; + case 3: + buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); + break; + default: + break; // other values ignored + } + } + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet The value to test + * @return true if the value is defined in the the base 64 alphabet, + * false otherwise. + * @since 1.4 + */ + public static boolean isBase64(byte octet) { + return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 + * alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the + * byte array is empty; + * false, otherwise + */ + public static boolean isArrayByteBase64(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 + * alphabet. + * + * @param arrayOctet byte array to test + * @return true if any byte is a valid character in the Base64 alphabet; false + * herwise + */ + private static boolean containsBase64Byte(byte[] arrayOctet) { + for (byte element : arrayOctet) { + if (isBase64(element)) { + return true; + } + } + return false; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF. + *

+ * For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}. + * + * @param binaryData binary data to encode + * @return String containing Base64 characters. + * @since 1.4 + */ + public static String encodeBase64String(byte[] binaryData) { + return newStringUtf8(encodeBase64(binaryData, true)); + } + + /** + * Encodes binary data using the base64 algorithm, without using chunking. + *

+ * For a chunking version, see {@link #encodeBase64String(byte[])}. + * + * @param binaryData binary data to encode + * @return String containing Base64 characters. + * @since 3.2 + */ + public static String encodeBase64StringUnChunked(byte[] binaryData) { + return newStringUtf8(encodeBase64(binaryData, false)); + } + + /** + * Encodes binary data using the base64 algorithm. + * + * @param binaryData binary data to encode + * @param useChunking whether to split the output into chunks + * @return String containing Base64 characters. + * @since 3.2 + */ + public static String encodeBase64String(byte[] binaryData, boolean useChunking) { + return newStringUtf8(encodeBase64(binaryData, useChunking)); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the + * output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the + * output. The + * url-safe variation emits - and _ instead of + and / characters. + * + * @param binaryData binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(byte[] binaryData) { + return newStringUtf8(encodeBase64(binaryData, false, true)); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character + * blocks + * + * @param binaryData binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Decodes a String containing containing characters in the Base64 alphabet. + * + * @param pArray A String containing Base64 character data + * @return a byte array containing binary data + * @since 1.4 + */ + public byte[] decode(String pArray) { + return decode(getBytesUtf8(pArray)); + } + + private byte[] getBytesUtf8(String pArray) { + try { + return pArray.getBytes("UTF8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * Decodes a byte[] containing containing characters in the Base64 alphabet. + * + * @param pArray A byte array containing Base64 character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + long len = (pArray.length * 3) / 4; + byte[] buf = new byte[(int) len]; + setInitialBuffer(buf, 0, buf.length); + decode(pArray, 0, pArray.length); + decode(pArray, 0, -1); // Notify decoder of EOF. + + // Would be nice to just return buf (like we sometimes do in the encode + // logic), but we have no idea what the line-length was (could even be + // variable). So we cannot determine ahead of time exactly how big an + // array is necessary. Hence the need to construct a 2nd byte array to + // hold the final result: + + byte[] result = new byte[pos]; + readResults(result, 0, result.length); + return result; + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 + * character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if true this encoder will chunk the base64 output into 76 + * character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than + * {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 + * character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if true this encoder will chunk the base64 output into 76 + * character blocks + * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / + * characters. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than + * {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 + * character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if true this encoder will chunk the base64 output into 76 + * character blocks + * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / + * characters. + * @param maxResultSize The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than + * maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, + int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, + isChunked ? CHUNK_SEPARATOR : EMPTY_BYTE_ARRAY); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maxium size of " + + maxResultSize); + } + + Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + return b64.encode(binaryData); + } + + /** + * Decodes a Base64 String into octets + * + * @param base64String String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(String base64String) { + return new Base64().decode(base64String); + } + + /** + * Decodes Base64 data into octets + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + return new Base64().decode(base64Data); + } + + /** + * Checks if a byte value is whitespace or not. + * + * @param byteToCheck the byte to check + * @return true if byte is whitespace, false otherwise + */ + private static boolean isWhiteSpace(byte byteToCheck) { + switch (byteToCheck) { + case ' ': + case '\n': + case '\r': + case '\t': + return true; + default: + return false; + } + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the Base64 + * alphabet. + * + * @param pArray a byte array containing binary data + * @return A String containing only Base64 character data + * @since 1.4 + */ + public String encodeToString(byte[] pArray) { + return newStringUtf8(encode(pArray)); + } + + private static String newStringUtf8(byte[] encode) { + String str = null; + try { + str = new String(encode, "UTF8"); + } catch (UnsupportedEncodingException ue) { + throw new RuntimeException(ue); + } + return str; + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 + * alphabet. + * + * @param pArray a byte array containing binary data + * @return A byte array containing only Base64 character data + */ + public byte[] encode(byte[] pArray) { + reset(); + if (pArray == null || pArray.length == 0) { + return pArray; + } + long len = getEncodeLength(pArray, lineLength, lineSeparator); + byte[] buf = new byte[(int) len]; + setInitialBuffer(buf, 0, buf.length); + encode(pArray, 0, pArray.length); + encode(pArray, 0, -1); // Notify encoder of EOF. + // Encoder might have resized, even though it was unnecessary. + if (buffer != buf) { + readResults(buf, 0, buf.length); + } + // In URL-SAFE mode we skip the padding characters, so sometimes our + // final length is a bit smaller. + if (isUrlSafe() && pos < buf.length) { + byte[] smallerBuf = new byte[pos]; + System.arraycopy(buf, 0, smallerBuf, 0, pos); + buf = smallerBuf; + } + return buf; + } + + /** + * Pre-calculates the amount of space needed to base64-encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * @param chunkSize line-length of the output (<= 0 means no chunking) between each + * chunkSeparator (e.g. CRLF). + * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). + * @return amount of space needed to encoded the supplied array. Returns + * a long since a max-len array will require Integer.MAX_VALUE + 33%. + */ + private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) { + // base64 always encodes to multiples of 4. + chunkSize = (chunkSize / 4) * 4; + + long len = (pArray.length * 4) / 3; + long mod = len % 4; + if (mod != 0) { + len += 4 - mod; + } + if (chunkSize > 0) { + boolean lenChunksPerfectly = len % chunkSize == 0; + len += (len / chunkSize) * chunkSeparator.length; + if (!lenChunksPerfectly) { + len += chunkSeparator.length; + } + } + return len; + } + + // Implementation of integer encoding used for crypto + + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param pArray a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature + * + * @param bigInt a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(BigInteger bigInt) { + if (bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger without sign bit. + * + * @param bigInt BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Resets this Base64 object to its initial newly constructed state. + */ + private void reset() { + buffer = null; + pos = 0; + readPos = 0; + currentLinePos = 0; + modulus = 0; + eof = false; + } + + // Getters for use in testing + + int getLineLength() { + return lineLength; + } + + byte[] getLineSeparator() { + return lineSeparator.clone(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/Charsets.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/Charsets.java new file mode 100644 index 00000000..5001fcc0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/Charsets.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.util; + +import java.nio.charset.Charset; + +/** + * Helps dealing with Charsets. + * + * @since 3.3 + */ +public class Charsets { + + /** + * Returns a charset object for the given charset name. + * + * @param charsetName The name of the requested charset; may be a canonical name, an alias, or + * null. If null, return the + * default charset. + * @return A charset object for the named charset + */ + public static Charset toCharset(String charsetName) { + return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); + } + + /** + * Returns a charset object for the given charset name. + * + * @param charsetName The name of the requested charset; may be a canonical name, an alias, or + * null. + * If null, return the default charset. + * @param defaultCharsetName the charset name to use if the requested charset is null + * @return A charset object for the named charset + */ + public static Charset toCharset(String charsetName, String defaultCharsetName) { + return charsetName == null ? Charset.forName(defaultCharsetName) : Charset.forName(charsetName); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java new file mode 100644 index 00000000..012493b0 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.util; + +import aria.apache.commons.net.io.Util; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Enumeration; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.X509ExtendedKeyManager; + +/** + * General KeyManager utilities + *

+ * How to use with a client certificate: + *

+ * KeyManager km = KeyManagerUtils.createClientKeyManager("JKS",
+ *     "/path/to/privatekeystore.jks","storepassword",
+ *     "privatekeyalias", "keypassword");
+ * FTPSClient cl = new FTPSClient();
+ * cl.setKeyManager(km);
+ * cl.connect(...);
+ * 
+ * If using the default store type and the key password is the same as the + * store password, these parameters can be omitted.
+ * If the desired key is the first or only key in the keystore, the keyAlias parameter + * can be omitted, in which case the code becomes: + *
+ * KeyManager km = KeyManagerUtils.createClientKeyManager(
+ *     "/path/to/privatekeystore.jks","storepassword");
+ * FTPSClient cl = new FTPSClient();
+ * cl.setKeyManager(km);
+ * cl.connect(...);
+ * 
+ * + * @since 3.0 + */ +public final class KeyManagerUtils { + + private static final String DEFAULT_STORE_TYPE = KeyStore.getDefaultType(); + + private KeyManagerUtils() { + // Not instantiable + } + + /** + * Create a client key manager which returns a particular key. + * Does not handle server keys. + * + * @param ks the keystore to use + * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key + * entry alias is used + * @param keyPass the password of the key to use + * @return the customised KeyManager + * @throws GeneralSecurityException if there is a problem creating the keystore + */ + public static KeyManager createClientKeyManager(KeyStore ks, String keyAlias, String keyPass) + throws GeneralSecurityException { + ClientKeyStore cks = + new ClientKeyStore(ks, keyAlias != null ? keyAlias : findAlias(ks), keyPass); + return new X509KeyManager(cks); + } + + /** + * Create a client key manager which returns a particular key. + * Does not handle server keys. + * + * @param storeType the type of the keyStore, e.g. "JKS" + * @param storePath the path to the keyStore + * @param storePass the keyStore password + * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key + * entry alias is used + * @param keyPass the password of the key to use + * @return the customised KeyManager + * @throws GeneralSecurityException if there is a problem creating the keystore + * @throws IOException if there is a problem creating the keystore + */ + public static KeyManager createClientKeyManager(String storeType, File storePath, + String storePass, String keyAlias, String keyPass) + throws IOException, GeneralSecurityException { + KeyStore ks = loadStore(storeType, storePath, storePass); + return createClientKeyManager(ks, keyAlias, keyPass); + } + + /** + * Create a client key manager which returns a particular key. + * Does not handle server keys. + * Uses the default store type and assumes the key password is the same as the store password + * + * @param storePath the path to the keyStore + * @param storePass the keyStore password + * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key + * entry alias is used + * @return the customised KeyManager + * @throws IOException if there is a problem creating the keystore + * @throws GeneralSecurityException if there is a problem creating the keystore + */ + public static KeyManager createClientKeyManager(File storePath, String storePass, String keyAlias) + throws IOException, GeneralSecurityException { + return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, keyAlias, storePass); + } + + /** + * Create a client key manager which returns a particular key. + * Does not handle server keys. + * Uses the default store type and assumes the key password is the same as the store password. + * The key alias is found by searching the keystore for the first private key entry + * + * @param storePath the path to the keyStore + * @param storePass the keyStore password + * @return the customised KeyManager + * @throws IOException if there is a problem creating the keystore + * @throws GeneralSecurityException if there is a problem creating the keystore + */ + public static KeyManager createClientKeyManager(File storePath, String storePass) + throws IOException, GeneralSecurityException { + return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, null, storePass); + } + + private static KeyStore loadStore(String storeType, File storePath, String storePass) + throws KeyStoreException, IOException, GeneralSecurityException { + KeyStore ks = KeyStore.getInstance(storeType); + FileInputStream stream = null; + try { + stream = new FileInputStream(storePath); + ks.load(stream, storePass.toCharArray()); + } finally { + Util.closeQuietly(stream); + } + return ks; + } + + private static String findAlias(KeyStore ks) throws KeyStoreException { + Enumeration e = ks.aliases(); + while (e.hasMoreElements()) { + String entry = e.nextElement(); + if (ks.isKeyEntry(entry)) { + return entry; + } + } + throw new KeyStoreException("Cannot find a private key entry"); + } + + private static class ClientKeyStore { + + private final X509Certificate[] certChain; + private final PrivateKey key; + private final String keyAlias; + + ClientKeyStore(KeyStore ks, String keyAlias, String keyPass) throws GeneralSecurityException { + this.keyAlias = keyAlias; + this.key = (PrivateKey) ks.getKey(this.keyAlias, keyPass.toCharArray()); + Certificate[] certs = ks.getCertificateChain(this.keyAlias); + X509Certificate[] X509certs = new X509Certificate[certs.length]; + for (int i = 0; i < certs.length; i++) { + X509certs[i] = (X509Certificate) certs[i]; + } + this.certChain = X509certs; + } + + final X509Certificate[] getCertificateChain() { + return this.certChain; + } + + final PrivateKey getPrivateKey() { + return this.key; + } + + final String getAlias() { + return this.keyAlias; + } + } + + private static class X509KeyManager extends X509ExtendedKeyManager { + + private final ClientKeyStore keyStore; + + X509KeyManager(final ClientKeyStore keyStore) { + this.keyStore = keyStore; + } + + // Call sequence: 1 + @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + return keyStore.getAlias(); + } + + // Call sequence: 2 + @Override public X509Certificate[] getCertificateChain(String alias) { + return keyStore.getCertificateChain(); + } + + @Override public String[] getClientAliases(String keyType, Principal[] issuers) { + return new String[] { keyStore.getAlias() }; + } + + // Call sequence: 3 + @Override public PrivateKey getPrivateKey(String alias) { + return keyStore.getPrivateKey(); + } + + @Override public String[] getServerAliases(String keyType, Principal[] issuers) { + return null; + } + + @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { + return null; + } + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/ListenerList.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/ListenerList.java new file mode 100644 index 00000000..2ddfa637 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/ListenerList.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.util; + +import java.io.Serializable; +import java.util.EventListener; +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + */ + +public class ListenerList implements Serializable, Iterable { + private static final long serialVersionUID = -1934227607974228213L; + + private final CopyOnWriteArrayList __listeners; + + public ListenerList() { + __listeners = new CopyOnWriteArrayList(); + } + + public void addListener(EventListener listener) { + __listeners.add(listener); + } + + public void removeListener(EventListener listener) { + __listeners.remove(listener); + } + + public int getListenerCount() { + return __listeners.size(); + } + + /** + * Return an {@link Iterator} for the {@link EventListener} instances. + * + * @return an {@link Iterator} for the {@link EventListener} instances + * @since 2.0 + * TODO Check that this is a good defensive strategy + */ + @Override public Iterator iterator() { + return __listeners.iterator(); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java new file mode 100644 index 00000000..ddec2139 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.util; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +/** + * General utilities for SSLContext. + * + * @since 3.0 + */ +public class SSLContextUtils { + + private SSLContextUtils() { + // Not instantiable + } + + /** + * Create and initialise an SSLContext. + * + * @param protocol the protocol used to instatiate the context + * @param keyManager the key manager, may be {@code null} + * @param trustManager the trust manager, may be {@code null} + * @return the initialised context. + * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs + */ + public static SSLContext createSSLContext(String protocol, KeyManager keyManager, + TrustManager trustManager) throws IOException { + return createSSLContext(protocol, keyManager == null ? null : new KeyManager[] { keyManager }, + trustManager == null ? null : new TrustManager[] { trustManager }); + } + + /** + * Create and initialise an SSLContext. + * + * @param protocol the protocol used to instatiate the context + * @param keyManagers the array of key managers, may be {@code null} but array entries must not be + * {@code null} + * @param trustManagers the array of trust managers, may be {@code null} but array entries must + * not be {@code null} + * @return the initialised context. + * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs + */ + public static SSLContext createSSLContext(String protocol, KeyManager[] keyManagers, + TrustManager[] trustManagers) throws IOException { + SSLContext ctx; + try { + ctx = SSLContext.getInstance(protocol); + ctx.init(keyManagers, trustManagers, /*SecureRandom*/ null); + } catch (GeneralSecurityException e) { + IOException ioe = new IOException("Could not initialize SSL context"); + ioe.initCause(e); + throw ioe; + } + return ctx; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java new file mode 100644 index 00000000..24f335ed --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package aria.apache.commons.net.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.net.ssl.SSLSocket; + +/** + * General utilities for SSLSocket. + * + * @since 3.4 + */ +public class SSLSocketUtils { + private SSLSocketUtils() { + // Not instantiable + } + + /** + * Enable the HTTPS endpoint identification algorithm on an SSLSocket. + * + * @param socket the SSL socket + * @return {@code true} on success (this is only supported on Java 1.7+) + */ + public static boolean enableEndpointNameVerification(SSLSocket socket) { + try { + Class cls = Class.forName("javax.net.ssl.SSLParameters"); + Method setEndpointIdentificationAlgorithm = + cls.getDeclaredMethod("setEndpointIdentificationAlgorithm", String.class); + Method getSSLParameters = SSLSocket.class.getDeclaredMethod("getSSLParameters"); + Method setSSLParameters = SSLSocket.class.getDeclaredMethod("setSSLParameters", cls); + if (setEndpointIdentificationAlgorithm != null + && getSSLParameters != null + && setSSLParameters != null) { + Object sslParams = getSSLParameters.invoke(socket); + if (sslParams != null) { + setEndpointIdentificationAlgorithm.invoke(sslParams, "HTTPS"); + setSSLParameters.invoke(socket, sslParams); + return true; + } + } + } catch (SecurityException e) { // Ignored + } catch (ClassNotFoundException e) { // Ignored + } catch (NoSuchMethodException e) { // Ignored + } catch (IllegalArgumentException e) { // Ignored + } catch (IllegalAccessException e) { // Ignored + } catch (InvocationTargetException e) { // Ignored + } + return false; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/SubnetUtils.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/SubnetUtils.java new file mode 100644 index 00000000..9dd2e22e --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/SubnetUtils.java @@ -0,0 +1,395 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package aria.apache.commons.net.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A class that performs some subnet calculations given a network address and a subnet mask. + * + * @see "http://www.faqs.org/rfcs/rfc1519.html" + * @since 2.0 + */ +public class SubnetUtils { + + private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})"; + private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})"; + private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS); + private static final Pattern cidrPattern = Pattern.compile(SLASH_FORMAT); + private static final int NBITS = 32; + + private int netmask = 0; + private int address = 0; + private int network = 0; + private int broadcast = 0; + + /** Whether the broadcast/network address are included in host count */ + private boolean inclusiveHostCount = false; + + /** + * Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16" + * + * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16" + * @throws IllegalArgumentException if the parameter is invalid, + * i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-3 decimal digits in range 1-32 + */ + public SubnetUtils(String cidrNotation) { + calculate(cidrNotation); + } + + /** + * Constructor that takes a dotted decimal address and a dotted decimal mask. + * + * @param address An IP address, e.g. "192.168.0.1" + * @param mask A dotted decimal netmask e.g. "255.255.0.0" + * @throws IllegalArgumentException if the address or mask is invalid, + * i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros + */ + public SubnetUtils(String address, String mask) { + calculate(toCidrNotation(address, mask)); + } + + /** + * Returns true if the return value of {@link SubnetInfo#getAddressCount()} + * includes the network and broadcast addresses. + * + * @return true if the hostcount includes the network and broadcast addresses + * @since 2.2 + */ + public boolean isInclusiveHostCount() { + return inclusiveHostCount; + } + + /** + * Set to true if you want the return value of {@link SubnetInfo#getAddressCount()} + * to include the network and broadcast addresses. + * + * @param inclusiveHostCount true if network and broadcast addresses are to be included + * @since 2.2 + */ + public void setInclusiveHostCount(boolean inclusiveHostCount) { + this.inclusiveHostCount = inclusiveHostCount; + } + + /** + * Convenience container for subnet summary information. + */ + public final class SubnetInfo { + /* Mask to convert unsigned int to a long (i.e. keep 32 bits) */ + private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL; + + private SubnetInfo() { + } + + private int netmask() { + return netmask; + } + + private int network() { + return network; + } + + private int address() { + return address; + } + + private int broadcast() { + return broadcast; + } + + // long versions of the values (as unsigned int) which are more suitable for range checking + private long networkLong() { + return network & UNSIGNED_INT_MASK; + } + + private long broadcastLong() { + return broadcast & UNSIGNED_INT_MASK; + } + + private int low() { + return (isInclusiveHostCount() ? network() + : broadcastLong() - networkLong() > 1 ? network() + 1 : 0); + } + + private int high() { + return (isInclusiveHostCount() ? broadcast() + : broadcastLong() - networkLong() > 1 ? broadcast() - 1 : 0); + } + + /** + * Returns true if the parameter address is in the + * range of usable endpoint addresses for this subnet. This excludes the + * network and broadcast adresses. + * + * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1" + * @return True if in range, false otherwise + */ + public boolean isInRange(String address) { + return isInRange(toInteger(address)); + } + + /** + * @param address the address to check + * @return true if it is in range + * @since 3.4 (made public) + */ + public boolean isInRange(int address) { + long addLong = address & UNSIGNED_INT_MASK; + long lowLong = low() & UNSIGNED_INT_MASK; + long highLong = high() & UNSIGNED_INT_MASK; + return addLong >= lowLong && addLong <= highLong; + } + + public String getBroadcastAddress() { + return format(toArray(broadcast())); + } + + public String getNetworkAddress() { + return format(toArray(network())); + } + + public String getNetmask() { + return format(toArray(netmask())); + } + + public String getAddress() { + return format(toArray(address())); + } + + /** + * Return the low address as a dotted IP address. + * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. + * + * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address + */ + public String getLowAddress() { + return format(toArray(low())); + } + + /** + * Return the high address as a dotted IP address. + * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. + * + * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address + */ + public String getHighAddress() { + return format(toArray(high())); + } + + /** + * Get the count of available addresses. + * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. + * + * @return the count of addresses, may be zero. + * @throws RuntimeException if the correct count is greater than {@code Integer.MAX_VALUE} + * @deprecated (3.4) use {@link #getAddressCountLong()} instead + */ + @Deprecated public int getAddressCount() { + long countLong = getAddressCountLong(); + if (countLong > Integer.MAX_VALUE) { + throw new RuntimeException("Count is larger than an integer: " + countLong); + } + // N.B. cannot be negative + return (int) countLong; + } + + /** + * Get the count of available addresses. + * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. + * + * @return the count of addresses, may be zero. + * @since 3.4 + */ + public long getAddressCountLong() { + long b = broadcastLong(); + long n = networkLong(); + long count = b - n + (isInclusiveHostCount() ? 1 : -1); + return count < 0 ? 0 : count; + } + + public int asInteger(String address) { + return toInteger(address); + } + + public String getCidrSignature() { + return toCidrNotation(format(toArray(address())), format(toArray(netmask()))); + } + + public String[] getAllAddresses() { + int ct = getAddressCount(); + String[] addresses = new String[ct]; + if (ct == 0) { + return addresses; + } + for (int add = low(), j = 0; add <= high(); ++add, ++j) { + addresses[j] = format(toArray(add)); + } + return addresses; + } + + /** + * {@inheritDoc} + * + * @since 2.2 + */ + @Override public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("CIDR Signature:\t[") + .append(getCidrSignature()) + .append("]") + .append(" Netmask: [") + .append(getNetmask()) + .append("]\n") + .append("Network:\t[") + .append(getNetworkAddress()) + .append("]\n") + .append("Broadcast:\t[") + .append(getBroadcastAddress()) + .append("]\n") + .append("First Address:\t[") + .append(getLowAddress()) + .append("]\n") + .append("Last Address:\t[") + .append(getHighAddress()) + .append("]\n") + .append("# Addresses:\t[") + .append(getAddressCount()) + .append("]\n"); + return buf.toString(); + } + } + + /** + * Return a {@link SubnetInfo} instance that contains subnet-specific statistics + * + * @return new instance + */ + public final SubnetInfo getInfo() { + return new SubnetInfo(); + } + + /* + * Initialize the internal fields from the supplied CIDR mask + */ + private void calculate(String mask) { + Matcher matcher = cidrPattern.matcher(mask); + + if (matcher.matches()) { + address = matchAddress(matcher); + + /* Create a binary netmask from the number of bits specification /x */ + int cidrPart = rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS); + for (int j = 0; j < cidrPart; ++j) { + netmask |= (1 << 31 - j); + } + + /* Calculate base network address */ + network = (address & netmask); + + /* Calculate broadcast address */ + broadcast = network | ~(netmask); + } else { + throw new IllegalArgumentException("Could not parse [" + mask + "]"); + } + } + + /* + * Convert a dotted decimal format address to a packed integer format + */ + private int toInteger(String address) { + Matcher matcher = addressPattern.matcher(address); + if (matcher.matches()) { + return matchAddress(matcher); + } else { + throw new IllegalArgumentException("Could not parse [" + address + "]"); + } + } + + /* + * Convenience method to extract the components of a dotted decimal address and + * pack into an integer using a regex match + */ + private int matchAddress(Matcher matcher) { + int addr = 0; + for (int i = 1; i <= 4; ++i) { + int n = (rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255)); + addr |= ((n & 0xff) << 8 * (4 - i)); + } + return addr; + } + + /* + * Convert a packed integer address into a 4-element array + */ + private int[] toArray(int val) { + int ret[] = new int[4]; + for (int j = 3; j >= 0; --j) { + ret[j] |= ((val >>> 8 * (3 - j)) & (0xff)); + } + return ret; + } + + /* + * Convert a 4-element array into dotted decimal format + */ + private String format(int[] octets) { + StringBuilder str = new StringBuilder(); + for (int i = 0; i < octets.length; ++i) { + str.append(octets[i]); + if (i != octets.length - 1) { + str.append("."); + } + } + return str.toString(); + } + + /* + * Convenience function to check integer boundaries. + * Checks if a value x is in the range [begin,end]. + * Returns x if it is in range, throws an exception otherwise. + */ + private int rangeCheck(int value, int begin, int end) { + if (value >= begin && value <= end) { // (begin,end] + return value; + } + + throw new IllegalArgumentException( + "Value [" + value + "] not in range [" + begin + "," + end + "]"); + } + + /* + * Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy + * see Hacker's Delight section 5.1 + */ + int pop(int x) { + x = x - ((x >>> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >>> 2) & 0x33333333); + x = (x + (x >>> 4)) & 0x0F0F0F0F; + x = x + (x >>> 8); + x = x + (x >>> 16); + return x & 0x0000003F; + } + + /* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format + * by counting the 1-bit population in the mask address. (It may be better to count + * NBITS-#trailing zeroes for this case) + */ + private String toCidrNotation(String addr, String mask) { + return addr + "/" + pop(toInteger(mask)); + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java new file mode 100644 index 00000000..a609f284 --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aria.apache.commons.net.util; + +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +/** + * TrustManager utilities for generating TrustManagers. + * + * @since 3.0 + */ +public final class TrustManagerUtils { + private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[] {}; + + private static class TrustManager implements X509TrustManager { + + private final boolean checkServerValidity; + + TrustManager(boolean checkServerValidity) { + this.checkServerValidity = checkServerValidity; + } + + /** + * Never generates a CertificateException. + */ + @Override public void checkClientTrusted(X509Certificate[] certificates, String authType) { + return; + } + + @Override public void checkServerTrusted(X509Certificate[] certificates, String authType) + throws CertificateException { + if (checkServerValidity) { + for (X509Certificate certificate : certificates) { + certificate.checkValidity(); + } + } + } + + /** + * @return an empty array of certificates + */ + @Override public X509Certificate[] getAcceptedIssuers() { + return EMPTY_X509CERTIFICATE_ARRAY; + } + } + + private static final X509TrustManager ACCEPT_ALL = new TrustManager(false); + + private static final X509TrustManager CHECK_SERVER_VALIDITY = new TrustManager(true); + + /** + * Generate a TrustManager that performs no checks. + * + * @return the TrustManager + */ + public static X509TrustManager getAcceptAllTrustManager() { + return ACCEPT_ALL; + } + + /** + * Generate a TrustManager that checks server certificates for validity, + * but otherwise performs no checks. + * + * @return the validating TrustManager + */ + public static X509TrustManager getValidateServerCertificateTrustManager() { + return CHECK_SERVER_VALIDITY; + } + + /** + * Return the default TrustManager provided by the JVM. + *

+ * This should be the same as the default used by + * {@link javax.net.ssl.SSLContext#init(KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} + * SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} + * when the TrustManager parameter is set to {@code null} + * + * @param keyStore the KeyStore to use, may be {@code null} + * @return the default TrustManager + * @throws GeneralSecurityException if an error occurs + */ + public static X509TrustManager getDefaultTrustManager(KeyStore keyStore) + throws GeneralSecurityException { + String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory instance = TrustManagerFactory.getInstance(defaultAlgorithm); + instance.init(keyStore); + return (X509TrustManager) instance.getTrustManagers()[0]; + } +} diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/util/package-info.java b/FtpComponent/src/main/java/aria/apache/commons/net/util/package-info.java new file mode 100644 index 00000000..7b95d75d --- /dev/null +++ b/FtpComponent/src/main/java/aria/apache/commons/net/util/package-info.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Utility classes + */ +package aria.apache.commons.net.util; \ No newline at end of file diff --git a/HttpComponent/bintray-release.gradle b/HttpComponent/bintray-release.gradle new file mode 100644 index 00000000..b222b779 --- /dev/null +++ b/HttpComponent/bintray-release.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.novoda.bintray-release' +publish { +// artifactId = 'aria-compiler' +// uploadName = 'AriaCompiler' + artifactId = 'httpComponent' + uploadName = 'HttpComponent' + userOrg = rootProject.ext.userOrg + groupId = rootProject.ext.groupId + publishVersion = rootProject.ext.publishVersion + desc = rootProject.ext.desc + website = rootProject.ext.website + licences = rootProject.ext.licences +} \ No newline at end of file diff --git a/HttpComponent/build.gradle b/HttpComponent/build.gradle index 290fc2ad..287ed5e8 100644 --- a/HttpComponent/build.gradle +++ b/HttpComponent/build.gradle @@ -30,3 +30,5 @@ dependencies { testImplementation 'junit:junit:4.12' implementation project(path: ':PublicComponent') } + +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/M3U8Component/bintray-release.gradle b/M3U8Component/bintray-release.gradle new file mode 100644 index 00000000..1bfb500d --- /dev/null +++ b/M3U8Component/bintray-release.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.novoda.bintray-release' +publish { +// artifactId = 'aria-compiler' +// uploadName = 'AriaCompiler' + artifactId = 'm3u8Component' + uploadName = 'M3U8Component' + userOrg = rootProject.ext.userOrg + groupId = rootProject.ext.groupId + publishVersion = rootProject.ext.publishVersion + desc = rootProject.ext.desc + website = rootProject.ext.website + licences = rootProject.ext.licences +} \ No newline at end of file diff --git a/M3U8Component/build.gradle b/M3U8Component/build.gradle index 6df0cb47..d44a132e 100644 --- a/M3U8Component/build.gradle +++ b/M3U8Component/build.gradle @@ -29,3 +29,5 @@ dependencies { implementation project(path: ':HttpComponent') implementation project(path: ':PublicComponent') } + +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..3cc74094 --- /dev/null +++ b/NOTICE @@ -0,0 +1,10 @@ +Aria +Copyright 2016 AriaLyy(https://github.com/AriaLyy/Aria) + +This project includes software from the commons-net project (Apache 2.0) +https://github.com/apache/commons-net + + +This project includes software from the jsch project (BSD 2.0) +* Copyright (c) 2002-2015 Atsuhiko Yamanaka, JCraft,Inc. +https://github.com/is/jsch diff --git a/PublicComponent/bintray-release.gradle b/PublicComponent/bintray-release.gradle new file mode 100644 index 00000000..8d66f20a --- /dev/null +++ b/PublicComponent/bintray-release.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.novoda.bintray-release' +publish { +// artifactId = 'aria-compiler' +// uploadName = 'AriaCompiler' + artifactId = 'publicComponent' + uploadName = 'PublicComponent' + userOrg = rootProject.ext.userOrg + groupId = rootProject.ext.groupId + publishVersion = rootProject.ext.publishVersion + desc = rootProject.ext.desc + website = rootProject.ext.website + licences = rootProject.ext.licences +} \ No newline at end of file diff --git a/PublicComponent/build.gradle b/PublicComponent/build.gradle index 55c66697..50ccd8df 100644 --- a/PublicComponent/build.gradle +++ b/PublicComponent/build.gradle @@ -27,3 +27,5 @@ dependencies { implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" testImplementation 'junit:junit:4.12' } + +apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java index 009c77e7..3676ca51 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java @@ -124,21 +124,23 @@ public class AriaConfig { .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { - cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { + @Override public void onLost(Network network) { + super.onLost(network); + isConnectedNet = false; + ALog.d(TAG, "onLost"); + } - @Override public void onLost(Network network) { - super.onLost(network); - isConnectedNet = false; - ALog.d(TAG, "onLost"); - } + @Override public void onAvailable(Network network) { + super.onAvailable(network); + ALog.d(TAG, "onAvailable"); + isConnectedNet = true; + } + }); + } - @Override public void onAvailable(Network network) { - super.onAvailable(network); - ALog.d(TAG, "onAvailable"); - isConnectedNet = true; - } - }); } public boolean isConnectedNet() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java index e82ec1e7..66512522 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core; import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.util.HashMap; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java new file mode 100644 index 00000000..02aee8d8 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.common; + +public enum ErrorCode { + ERROR_CODE_NORMAL(0, "正常"), ERROR_CODE_TASK_ID_NULL(1, "任务id为空的错误码"), + ERROR_CODE_URL_NULL(2, "url 为空"), ERROR_CODE_URL_INVALID(3, "url 无效"), + ERROR_CODE_PAGE_NUM(4, "page和num不能小于1"), ERROR_CODE_GROUP_URL_NULL(5, "组合任务url列表为空"), + ERROR_CODE_UPLOAD_FILE_NULL(7, "上传文件不存在"), + ERROR_CODE_MEMBER_WARNING(8, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); + + int code; + String msg; + + ErrorCode(int code, String msg) { + this.code = code; + this.msg = msg; + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index ff15fc02..490715bb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -184,19 +184,6 @@ public class RecordHandler implements IRecordHandler { ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size())); } - /** - * 获取记录类型 - * - * @return {@link #TYPE_DOWNLOAD}、{@link #TYPE_UPLOAD} - */ - private int getRecordType() { - if (mEntity instanceof DownloadEntity) { - return TYPE_DOWNLOAD; - } else { - return TYPE_UPLOAD; - } - } - /** * 获取任务路径 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java index 4d8e6277..de480d57 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IFtpUploadInterceptor.java @@ -21,26 +21,6 @@ import java.util.List; /** * FTP文件上传拦截器,如果远端已有同名文件,可使用该拦截器控制覆盖文件或修改该文件上传到服务器端端文件名 - *

- *   
- *     Aria.upload(this)
- *         .loadFtp(FILE_PATH)
- *         .setUploadUrl(URL)
- *         .setUploadInterceptor(
- *             new IFtpUploadInterceptor() {
- *
- *               @Override
- *               public FtpInterceptHandler onIntercept(UploadEntity entity, List fileList) {
- *                 FtpInterceptHandler.Builder builder = new FtpInterceptHandler.Builder();
- *                 builder.coverServerFile();
- *                 //builder.resetFileName("test");
- *                 return builder.build();
- *               }
- *          })
- *         .create();
- *   
- *
- * 
*/ public interface IFtpUploadInterceptor extends IEventHandler { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index a2b283e9..0966b12d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.wrapper; import com.arialyy.aria.core.TaskOptionParams; import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.config.BaseTaskConfig; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; @@ -25,6 +26,8 @@ import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.util.ComponentUtil; +import java.util.ArrayList; +import java.util.List; /** * Created by lyy on 2017/2/23. 所有任务实体的父类 @@ -84,6 +87,15 @@ public abstract class AbsTaskWrapper */ private TaskOptionParams optionParams = new TaskOptionParams(); + /** + * 初始状态列表 + */ + private List errorCode = new ArrayList<>(); + + public List getErrorCode() { + return errorCode; + } + public void setTaskOption(ITaskOption option) { this.taskOption = option; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 8e786b6a..c479c525 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -17,6 +17,7 @@ package com.arialyy.aria.util; import android.text.TextUtils; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.upload.UTaskWrapper; @@ -39,7 +40,7 @@ public class CheckUtil { public static void checkMemberClass(Class clazz) { int modifiers = clazz.getModifiers(); if (!clazz.isMemberClass() || !Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers)) { - ALog.e(TAG, "为了放置内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); + ALog.e(TAG, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); } } @@ -53,22 +54,14 @@ public class CheckUtil { if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); } - /** - * 检测下载链接是否为null - */ - public static void checkPath(String path) { - if (TextUtils.isEmpty(path)) { - throw new IllegalArgumentException("保存路径不能为null"); - } - } - /** * 检测url是否合法,如果url不合法,将抛异常 */ - public static void checkTaskId(long taskId) { + public static ErrorCode checkTaskId(long taskId) { if (taskId < 0) { - throw new IllegalArgumentException("任务id不能小于0"); + return ErrorCode.ERROR_CODE_TASK_ID_NULL; } + return ErrorCode.ERROR_CODE_NORMAL; } /** @@ -154,38 +147,6 @@ public class CheckUtil { } } - /** - * 检查命令实体 - * - * @param checkType 删除命令和停止命令不需要检查下载链接和保存路径 - * @return {@code false}实体无效 - */ - public static boolean checkCmdEntity(AbsTaskWrapper entity, boolean checkType) { - boolean b = false; - if (entity instanceof DTaskWrapper) { - DownloadEntity entity1 = ((DTaskWrapper) entity).getEntity(); - if (entity1 == null) { - ALog.e(TAG, "下载实体不能为空"); - } else if (checkType && TextUtils.isEmpty(entity1.getUrl())) { - ALog.e(TAG, "下载链接不能为空"); - } else if (checkType && TextUtils.isEmpty(entity1.getDownloadPath())) { - ALog.e(TAG, "保存路径不能为空"); - } else { - b = true; - } - } else if (entity instanceof UTaskWrapper) { - UploadEntity entity1 = ((UTaskWrapper) entity).getEntity(); - if (entity1 == null) { - ALog.e(TAG, "上传实体不能为空"); - } else if (TextUtils.isEmpty(entity1.getFilePath())) { - ALog.e(TAG, "上传文件路径不能为空"); - } else { - b = true; - } - } - return b; - } - /** * 检查上传实体是否合法 */ diff --git a/app/build.gradle b/app/build.gradle index fa553264..d933ece2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,23 +58,20 @@ dependencies { testImplementation 'junit:junit:4.12' implementation 'androidx.cardview:cardview:1.0.0' implementation 'com.google.android.material:material:1.0.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib:${rootProject.ext.kotlin_version}" - implementation project(':Aria') - kapt project(':AriaCompiler') - // api 'com.arialyy.aria:aria-core:3.6.4' - // kapt 'com.arialyy.aria:aria-compiler:3.6.4' - - // compile 'com.arialyy.aria:aria-core:3.3.14' - // annotationProcessor 'com.arialyy.aria:aria-compiler:3.3.14' api 'com.github.PhilJay:MPAndroidChart:v3.0.3' - implementation project(':AppFrame') debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' implementation 'com.github.bumptech.glide:glide:3.7.0' implementation 'com.pddstudio:highlightjs-android:1.5.0' implementation 'org.greenrobot:eventbus:3.1.1' - implementation project(path: ':M3U8Component') - implementation project(path: ':FtpComponent') + + // aria + kapt project(':AriaCompiler') + implementation project(':Aria') + implementation project(':AppFrame') + implementation project(':M3U8Component') + implementation project(':FtpComponent') + implementation project(path: ':AriaAnnotations') } repositories { mavenCentral() diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index af36efe6..e4ea2d42 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -5,14 +5,14 @@ - + - + - + @@ -27,27 +27,27 @@ - + - + - + @@ -113,10 +113,10 @@ - + - + @@ -131,7 +131,7 @@ - + @@ -158,4 +158,4 @@ - \ No newline at end of file + diff --git a/app/src/main/assets/aria_config_1.xml b/app/src/main/assets/aria_config_1.xml new file mode 100644 index 00000000..54198495 --- /dev/null +++ b/app/src/main/assets/aria_config_1.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java index d082101e..15d6f2fc 100644 --- a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java +++ b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java @@ -21,7 +21,7 @@ import android.os.StrictMode; import com.arialyy.aria.core.Aria; import com.arialyy.frame.core.AbsFrame; import com.arialyy.simple.BuildConfig; -import com.squareup.leakcanary.LeakCanary; +//import com.squareup.leakcanary.LeakCanary; /** * Created by Lyy on 2016/9/27. @@ -42,12 +42,12 @@ public class BaseApplication extends Application { // .build()); //StrictMode.setThreadPolicy( // new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); - if (LeakCanary.isInAnalyzerProcess(this)) {//1 - //This process is dedicated to LeakCanary for heap analysis. - //You should not init your app in this process. - return; - } - LeakCanary.install(this); + //if (LeakCanary.isInAnalyzerProcess(this)) {//1 + // //This process is dedicated to LeakCanary for heap analysis. + // //You should not init your app in this process. + // return; + //} + //LeakCanary.install(this); } //registerReceiver(new ConnectionChangeReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); diff --git a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt index 8cf91a69..495a39c0 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt +++ b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt @@ -292,7 +292,6 @@ class KotlinDownloadActivity : BaseActivity() { //.addHeader("Accept-Encoding", "gzip, deflate") //.addHeader("DNT", "1") //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") - .useServerFileName(true) .setFilePath(mFilePath!!, true) .create() } diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 9157ad80..1bc32a7f 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -32,7 +32,6 @@ import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.common.controller.ControllerType; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.listener.ISchedulers; @@ -289,12 +288,12 @@ public class SingleTaskActivity extends BaseActivity { private void startD() { HttpOption option = new HttpOption(); - option.addHeader("1", "@"); + option.addHeader("1", "@") + .setFileLenAdapter(new FileLenAdapter()) + .useServerFileName(true); mTaskId = Aria.download(SingleTaskActivity.this) .load(mUrl) - .useServerFileName(true) .setFilePath(mFilePath, true) - .setFileLenAdapter(new FileLenAdapter()) .option(option) .create(); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 13256730..def2c8af 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -21,6 +21,7 @@ import android.util.Log; import android.view.View; import com.arialyy.annotations.DownloadGroup; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; @@ -99,18 +100,7 @@ public class DownloadGroupActivity extends BaseActivity> headers) { - - List sLength = headers.get("Content-Length"); - if (sLength == null || sLength.isEmpty()) { - return -1; - } - String temp = sLength.get(0); - - return Long.parseLong(temp); - } - }) + .option(getHttpOption()) .setFileSize(114981416) //.updateUrls(temp) .create(); @@ -132,6 +122,12 @@ public class DownloadGroupActivity extends BaseActivity> headers) { + + List sLength = headers.get("Content-Length"); + if (sLength == null || sLength.isEmpty()) { + return -1; + } + String temp = sLength.get(0); + + return Long.parseLong(temp); + } + } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java index f0e7daf8..44767194 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java @@ -237,7 +237,6 @@ public class M3U8LiveDLoadActivity extends BaseActivity private void startD() { Aria.download(M3U8LiveDLoadActivity.this) .load(mUrl) - .useServerFileName(true) .setFilePath(mFilePath, true) .m3u8LiveOption(getLiveoption()) .create(); diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index 23c93312..24ac2af3 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -295,7 +295,6 @@ public class M3U8VodDLoadActivity extends BaseActivity { private void startD() { mTaskId = Aria.download(M3U8VodDLoadActivity.this) .load(mUrl) - .useServerFileName(true) .setFilePath(mFilePath, true) .m3u8VodOption(getM3U8Option()) .create(); diff --git a/build.gradle b/build.gradle index 2a99294f..1f82c71c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.20' +// ext.kotlin_version = '1.3.20' + ext.kotlin_version = '1.3.41' repositories { jcenter() mavenCentral() @@ -9,8 +10,10 @@ buildscript { } dependencies { // classpath 'com.android.tools.build:gradle:2.3.3' - classpath 'com.android.tools.build:gradle:3.3.2' - classpath 'com.novoda:bintray-release:0.9' +// classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.novoda:bintray-release:0.9.1' +// classpath 'com.novoda:bintray-release:0.9' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" // classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' // classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' @@ -41,13 +44,14 @@ task clean(type: Delete) { } ext { - versionCode = 370 - versionName = '3.7' + versionCode = 371 + versionName = '3.7_pre_2' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName // publishVersion = '1.0.4' //FTP插件 repoName='maven' +// repoName='aria' desc = 'android 下载框架' website = 'https://github.com/AriaLyy/Aria' licences = ['Apache-2.0'] diff --git a/gradle.properties b/gradle.properties index 8f03aa05..77a70819 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,6 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Wed Dec 07 20:19:22 CST 2016 -#org.gradle.daemon=true -#org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 # gradle proxy https://chaosleong.github.io/2017/02/10/Configuring-Gradle-Proxy/ #systemProp.socksProxyHost=127.0.0.1 #systemProp.socksProxyPort=1080 @@ -24,4 +22,7 @@ # androidx https://developer.android.com/studio/preview/features/?hl=zh-cn#androidx_migration android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true + +#org.gradle.daemon=true +#org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cc627917..440edac9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip From 2b0fcbb98f6167d8e126b4163f8436c3502a8c0f Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 21 Oct 2019 19:55:55 +0800 Subject: [PATCH 009/140] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=BF=A1=E6=81=AF=E4=B8=BB=E5=8A=A8=E6=8A=9B?= =?UTF-8?q?=E5=87=BA=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/arialyy/aria/core/Aria.java | 12 +- .../com/arialyy/aria/core/AriaManager.java | 143 +++++++++++++----- .../arialyy/aria/core/WidgetLiftManager.java | 18 +-- .../aria/core/download/CheckDEntityUtil.java | 8 +- .../aria/core/download/CheckDGEntityUtil.java | 3 +- .../core/download/CheckFtpDirEntityUtil.java | 3 +- .../aria/core/download/DownloadReceiver.java | 40 ++--- .../target/AbsGroupConfigHandler.java | 14 +- .../download/target/DNormalConfigHandler.java | 12 +- .../download/target/FtpBuilderTarget.java | 3 +- .../core/scheduler/FailureTaskHandler.java | 2 +- .../aria/core/scheduler/TaskSchedulers.java | 40 ++--- .../aria/core/upload/CheckUEntityUtil.java | 5 +- .../aria/core/upload/UploadReceiver.java | 6 +- .../upload/target/UNormalConfigHandler.java | 18 ++- .../arialyy/aria/http/HttpFileInfoThread.java | 2 +- .../com/arialyy/aria/m3u8/M3U8InfoThread.java | 2 +- .../arialyy/aria/core/common/ErrorCode.java | 7 +- .../core/download/AbsGroupTaskWrapper.java | 14 -- .../aria/core/download/DownloadEntity.java | 4 +- .../aria/core/task/DownloadGroupTask.java | 1 - .../aria/core/wrapper/AbsTaskWrapper.java | 12 -- .../aria/core/wrapper/ITaskWrapper.java | 2 +- .../com/arialyy/aria/orm/ActionPolicy.java | 2 +- .../com/arialyy/aria/orm/DelegateCommon.java | 2 +- .../java/com/arialyy/aria/util/CheckUtil.java | 105 +------------ .../java/com/arialyy/simple/MainActivity.java | 1 + .../core/download/FtpDownloadActivity.java | 6 +- .../download/fragment/DownloadFragment.java | 2 +- .../download/fragment/FragmentActivity.java | 1 - 30 files changed, 227 insertions(+), 263 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/Aria.java b/Aria/src/main/java/com/arialyy/aria/core/Aria.java index fb218198..11f2c63d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/Aria.java +++ b/Aria/src/main/java/com/arialyy/aria/core/Aria.java @@ -24,8 +24,6 @@ import android.app.Service; import android.content.Context; import android.os.Build; import android.widget.PopupWindow; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.upload.UploadReceiver; import com.arialyy.aria.util.ALog; @@ -136,14 +134,8 @@ import com.arialyy.aria.util.ALog; return (Service) obj; } else if (obj instanceof Activity) { return (Activity) obj; - } else if (obj instanceof DialogFragment) { - return ((DialogFragment) obj).getContext(); - } else if (obj instanceof android.app.DialogFragment) { - return ((android.app.DialogFragment) obj).getActivity(); - } else if (obj instanceof Fragment) { - return ((Fragment) obj).getContext(); - } else if (obj instanceof android.app.Fragment) { - return ((android.app.Fragment) obj).getActivity(); + } else if (AriaManager.isFragment(obj.getClass())) { + return AriaManager.getFragmentActivity(obj); } else if (obj instanceof Dialog) { return ((Dialog) obj).getContext(); } else if (obj instanceof PopupWindow) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index 3461574a..dc988c96 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -21,17 +21,11 @@ import android.app.Activity; import android.app.Application; import android.app.Dialog; import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.widget.PopupWindow; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; import com.arialyy.aria.core.command.CommandManager; import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.config.AppConfig; @@ -53,6 +47,8 @@ import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.AriaCrashHandler; import com.arialyy.aria.util.RecordUtil; import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -60,13 +56,18 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * Created by lyy on 2016/12/1. https://github.com/AriaLyy/Aria Aria管理器,任务操作在这里执行 + * Created by lyy on 2016/12/1. https://github.com/AriaLyy/Aria + * Aria管理器,任务操作在这里执行 */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class AriaManager { private static final String TAG = "AriaManager"; private static final Object LOCK = new Object(); - + /** + * android、androidx、support的fragment、dialogFragment类名 + */ + private static List mFragmentClassName = new ArrayList<>(); + private static List mDialogFragmentClassName = new ArrayList<>(); @SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null; private Map mReceivers = new ConcurrentHashMap<>(); @@ -79,6 +80,19 @@ import java.util.concurrent.ConcurrentHashMap; private DelegateWrapper mDbWrapper; private AriaConfig mConfig; + static { + mFragmentClassName.add("androidx.fragment.app.Fragment"); + mFragmentClassName.add("androidx.fragment.app.DialogFragment"); + mFragmentClassName.add("android.app.Fragment"); + mFragmentClassName.add("android.app.DialogFragment"); + mFragmentClassName.add("android.support.v4.app.Fragment"); + mFragmentClassName.add("android.support.v4.app.DialogFragment"); + + mDialogFragmentClassName.add("androidx.fragment.app.DialogFragment"); + mDialogFragmentClassName.add("android.app.DialogFragment"); + mDialogFragmentClassName.add("android.support.v4.app.DialogFragment"); + } + private AriaManager(Context context) { APP = context.getApplicationContext(); } @@ -114,8 +128,6 @@ import java.util.concurrent.ConcurrentHashMap; return APP; } - - /** * 初始化数据库 */ @@ -166,8 +178,6 @@ import java.util.concurrent.ConcurrentHashMap; return mAriaHandler; } - - public Map getReceiver() { return mReceivers; } @@ -303,25 +313,12 @@ import java.util.concurrent.ConcurrentHashMap; needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj); } else if (obj instanceof PopupWindow) { needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj); - } else if (obj instanceof DialogFragment) { - needRmReceiver = widgetLiftManager.handleDialogFragmentLift((DialogFragment) obj); - } else if (obj instanceof android.app.DialogFragment) { - needRmReceiver = widgetLiftManager.handleDialogFragmentLift((android.app.DialogFragment) obj); + } else if (isDialogFragment(obj.getClass())) { + needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj)); } - if (receiver == null) { - AbsReceiver absReceiver; - switch (type) { - case ReceiverType.DOWNLOAD: - absReceiver = new DownloadReceiver(); - break; - case ReceiverType.UPLOAD: - absReceiver = new UploadReceiver(); - break; - default: - absReceiver = new DownloadReceiver(); - break; - } + AbsReceiver absReceiver = + type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver() : new UploadReceiver(); absReceiver.targetName = obj.getClass().getName(); AbsReceiver.OBJ_MAP.put(absReceiver.getKey(), obj); absReceiver.needRmListener = needRmReceiver; @@ -339,14 +336,8 @@ import java.util.concurrent.ConcurrentHashMap; * @return {@link #createKey(String, Object)} */ private String getKey(@ReceiverType String type, Object obj) { - if (obj instanceof DialogFragment) { - relateSubClass(type, obj, ((DialogFragment) obj).getActivity()); - } else if (obj instanceof android.app.DialogFragment) { - relateSubClass(type, obj, ((android.app.DialogFragment) obj).getActivity()); - } else if (obj instanceof Fragment) { - relateSubClass(type, obj, ((Fragment) obj).getActivity()); - } else if (obj instanceof android.app.Fragment) { - relateSubClass(type, obj, ((android.app.Fragment) obj).getActivity()); + if (isFragment(obj.getClass())) { + relateSubClass(type, obj, getFragmentActivity(obj)); } else if (obj instanceof Dialog) { Activity activity = ((Dialog) obj).getOwnerActivity(); if (activity != null) { @@ -361,10 +352,86 @@ import java.util.concurrent.ConcurrentHashMap; return createKey(type, obj); } + /** + * 获取fragment的activity + * + * @return 获取失败,返回null + */ + static Activity getFragmentActivity(Object obj) { + try { + Method method = obj.getClass().getMethod("getActivity"); + return (Activity) method.invoke(obj); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 判断注解对象是否是fragment + * + * @return true 对象是fragment + */ + static boolean isFragment(Class subClazz) { + Class parentClass = subClazz.getSuperclass(); + if (parentClass == null) { + return false; + } else { + String parentName = parentClass.getName(); + if (mFragmentClassName.contains(parentName)) { + return true; + } else { + return isFragment(parentClass); + } + } + } + + /** + * 判断对象是否是DialogFragment + * + * @return true 对象是DialogFragment + */ + private boolean isDialogFragment(Class subClazz) { + Class parentClass = subClazz.getSuperclass(); + if (parentClass == null) { + return false; + } else { + String parentName = parentClass.getName(); + if (mFragmentClassName.contains(parentName)) { + return true; + } else { + return isDialogFragment(parentClass); + } + } + } + + /** + * 获取DialogFragment的dialog + * + * @return 获取失败,返回null + */ + private Dialog getDialog(Object obj) { + try { + Method method = obj.getClass().getMethod("getDialog"); + return (Dialog) method.invoke(obj); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + /** * 关联Activity类和Fragment间的关系 * - * @param sub Frgament或dialog类 + * @param sub Fragment或dialog类 * @param activity activity寄主类 */ private void relateSubClass(@ReceiverType String type, Object sub, Activity activity) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java b/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java index 2dae53cb..f19b5ab6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java @@ -21,7 +21,6 @@ import android.content.DialogInterface; import android.os.Build; import android.os.Message; import android.widget.PopupWindow; -import androidx.fragment.app.DialogFragment; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; @@ -35,22 +34,9 @@ final class WidgetLiftManager { /** * 处理DialogFragment事件 - * - * @param dialogFragment {@link android.app.DialogFragment} */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) boolean handleDialogFragmentLift( - android.app.DialogFragment dialogFragment) { - return handleDialogLift(dialogFragment.getDialog()); - } - - /** - * 处理DialogFragment事件 - * - * @param dialogFragment {@link DialogFragment} - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) boolean handleDialogFragmentLift( - DialogFragment dialogFragment) { - return handleDialogLift(dialogFragment.getDialog()); + @TargetApi(Build.VERSION_CODES.HONEYCOMB) boolean handleDialogFragmentLift(Dialog dialog) { + return handleDialogLift(dialog); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 141d4f31..6fffb0d7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.IRecordHandler; @@ -24,6 +25,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; import java.io.File; @@ -31,7 +33,7 @@ import java.io.File; * 检查下载任务实体 */ public class CheckDEntityUtil implements ICheckEntityUtil { - private final String TAG = "CheckDLoadEntity"; + private final String TAG = CommonUtil.getClassName(getClass()); private DTaskWrapper mWrapper; private DownloadEntity mEntity; @@ -47,7 +49,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { @Override public boolean checkEntity() { if (mWrapper.getErrorEvent() != null) { - ALog.e(TAG, mWrapper.getErrorEvent().errorMsg); + ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg)); return false; } @@ -179,7 +181,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { if (TextUtils.isEmpty(url)) { ALog.e(TAG, "下载失败,url为null"); return false; - } else if (!CheckUtil.checkUrlNotThrow(url)) { + } else if (!CheckUtil.checkUrl(url)) { ALog.e(TAG, "下载失败,url【" + url + "】错误"); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 3fd4f450..5af68983 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; @@ -113,7 +114,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { @Override public boolean checkEntity() { if (mWrapper.getErrorEvent() != null) { - ALog.e(TAG, mWrapper.getErrorEvent().errorMsg); + ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg)); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java index 9db5605b..8f1cdf88 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; @@ -77,7 +78,7 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil { @Override public boolean checkEntity() { if (mWrapper.getErrorEvent() != null) { - ALog.e(TAG, mWrapper.getErrorEvent().errorMsg); + ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg)); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 0ebcb131..4f91b0db 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -20,9 +20,10 @@ import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.CancelAllCmd; +import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.common.ErrorCode; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.download.target.DTargetFactory; import com.arialyy.aria.core.download.target.FtpBuilderTarget; @@ -34,15 +35,13 @@ import com.arialyy.aria.core.download.target.GroupNormalTarget; import com.arialyy.aria.core.download.target.HttpBuilderTarget; import com.arialyy.aria.core.download.target.HttpNormalTarget; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.scheduler.TaskSchedulers; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; -import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.ComponentUtil; import com.arialyy.aria.util.DbDataHelper; @@ -77,7 +76,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public HttpBuilderTarget load(@NonNull String url) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkUrlInvalidThrow(url); return DTargetFactory.getInstance() .generateBuilderTarget(HttpBuilderTarget.class, url); } @@ -91,7 +89,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public HttpNormalTarget load(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(HttpNormalTarget.class, taskId); } @@ -104,7 +101,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public GroupBuilderTarget loadGroup(List urls) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkDownloadUrls(urls); return DTargetFactory.getInstance().generateGroupBuilderTarget(urls); } @@ -117,7 +113,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public GroupNormalTarget loadGroup(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(GroupNormalTarget.class, taskId); } @@ -128,7 +123,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpBuilderTarget loadFtp(@NonNull String url) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkUrlInvalidThrow(url); return DTargetFactory.getInstance() .generateBuilderTarget(FtpBuilderTarget.class, url); } @@ -142,7 +136,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpNormalTarget loadFtp(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(FtpNormalTarget.class, taskId); } @@ -153,7 +146,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpDirBuilderTarget loadFtpDir(@NonNull String dirUrl) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkUrlInvalidThrow(dirUrl); return DTargetFactory.getInstance().generateDirBuilderTarget(dirUrl); } @@ -166,7 +158,6 @@ public class DownloadReceiver extends AbsReceiver { @CheckResult public FtpDirNormalTarget loadFtpDir(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkTaskId(taskId); return DTargetFactory.getInstance() .generateNormalTarget(FtpDirNormalTarget.class, taskId); } @@ -244,7 +235,10 @@ public class DownloadReceiver extends AbsReceiver { * @param taskId 任务实体唯一id */ public DownloadEntity getDownloadEntity(long taskId) { - CheckUtil.checkTaskId(taskId); + if (taskId < 0) { + ALog.e(TAG, "taskId错误"); + return null; + } return DbEntity.findFirst(DownloadEntity.class, "rowid=?", String.valueOf(taskId)); } @@ -255,7 +249,9 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果url错误或查找不到数据,则返回null */ public DownloadEntity getFirstDownloadEntity(String downloadUrl) { - CheckUtil.checkUrl(downloadUrl); + if (!CheckUtil.checkUrl(downloadUrl)) { + return null; + } return DbEntity.findFirst(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); } @@ -265,7 +261,9 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果url错误或查找不到数据,则返回null */ public List getDownloadEntity(String downloadUrl) { - CheckUtil.checkUrl(downloadUrl); + if (!CheckUtil.checkUrl(downloadUrl)) { + return null; + } return DbEntity.findDatas(DownloadEntity.class, "url=? and isGroupChild='false'", downloadUrl); } @@ -276,7 +274,9 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果实体不存在,返回null */ public DownloadGroupEntity getGroupEntity(long taskId) { - CheckUtil.checkTaskId(taskId); + if (taskId < 0) { + ALog.e(TAG, "任务Id错误"); + } return DbDataHelper.getDGEntity(taskId); } @@ -287,7 +287,9 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果实体不存在,返回null */ public DownloadGroupEntity getGroupEntity(List urls) { - CheckUtil.checkDownloadUrls(urls); + if (CheckUtil.checkDownloadUrlsIsEmpty(urls)){ + return null; + } return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls)); } @@ -298,7 +300,9 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果实体不存在,返回null */ public DownloadGroupEntity getFtpDirEntity(String url) { - CheckUtil.checkUrl(url); + if (!CheckUtil.checkUrl(url)) { + return null; + } return DbDataHelper.getDGEntity(url); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java index e0fa063b..617848f6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java @@ -17,15 +17,17 @@ package com.arialyy.aria.core.download.target; import android.text.TextUtils; import androidx.annotation.CheckResult; +import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IConfigHandler; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.CommonUtil; @@ -44,9 +46,15 @@ abstract class AbsGroupConfigHandler implements IConfi TAG = CommonUtil.getClassName(getClass()); mTarget = target; mWrapper = TaskWrapperManager.getInstance().getGroupWrapper(DGTaskWrapper.class, taskId); - if (taskId != -1 && mWrapper.getEntity().getId() == -1) { - mWrapper.setErrorEvent(new ErrorEvent(taskId, String.format("没有id为%s的任务", taskId))); + // 判断已存在的任务 + if (mTarget instanceof AbsNormalTarget) { + if (taskId < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务id为空")); + } else if (mWrapper.getEntity().getId() < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务信息不存在")); + } } + mTarget.setTaskWrapper(mWrapper); if (getEntity() != null) { getTaskWrapper().setDirPathTemp(getEntity().getDirPath()); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java index 32343bc8..c5e24802 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.target; import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.event.ErrorEvent; @@ -47,11 +48,16 @@ class DNormalConfigHandler implements IConfigHandler { private void initTarget(long taskId) { mWrapper = TaskWrapperManager.getInstance().getNormalTaskWrapper(DTaskWrapper.class, taskId); - if (taskId != -1 && mWrapper.getEntity().getId() == -1) { - mWrapper.setErrorEvent(new ErrorEvent(taskId, String.format("没有id为%s的任务", taskId))); + // 判断已存在的任务 + if (mTarget instanceof AbsNormalTarget) { + if (taskId < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务id为空")); + } else if (mWrapper.getEntity().getId() < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务信息不存在")); + } } - mEntity = mWrapper.getEntity(); + mEntity = mWrapper.getEntity(); mTarget.setTaskWrapper(mWrapper); if (mEntity != null) { getWrapper().setTempFilePath(mEntity.getFilePath()); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 6bb56047..0baadabe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -39,7 +39,8 @@ public class FtpBuilderTarget extends AbsBuilderTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) + @com.arialyy.aria.core.common.CheckResult(suggest = Suggest.TASK_CONTROLLER) + //@CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java index 3b5873e1..1988cba7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java @@ -84,7 +84,7 @@ class FailureTaskHandler { private void retryTask(final TASK task) { long interval = task.getTaskWrapper().getConfig().getReTryInterval(); final int num = task.getTaskWrapper().getConfig().getReTryNum(); - final ITaskQueue queue = mSchedulers.getQueue(task); + final ITaskQueue queue = mSchedulers.getQueue(task.getTaskType()); mAriaManager.getAriaHandler().postDelayed(new Runnable() { @Override public void run() { AbsEntity entity = task.getTaskWrapper().getEntity(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java index 771040c5..7ba7fad0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java @@ -20,21 +20,21 @@ import android.os.Bundle; import android.os.Message; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.task.DownloadGroupTask; -import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.AbsNormalEntity; -import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.listener.ISchedulers; -import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.inf.TaskSchedulerType; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.DGroupTaskQueue; import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.queue.ITaskQueue; import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.NetUtils; @@ -74,10 +74,9 @@ public class TaskSchedulers implements ISchedulers { /** * 通过任务类型获取任务队列 * - * @param curTask 当前接收到的的任务 + * @param taskType 任务类型 */ - ITaskQueue getQueue(TASK curTask) { - int taskType = curTask.getTaskType(); + ITaskQueue getQueue(int taskType) { if (taskType == ITask.DOWNLOAD) { return DTaskQueue.getInstance(); } @@ -171,6 +170,11 @@ public class TaskSchedulers implements ISchedulers { } @Override public boolean handleMessage(Message msg) { + if (msg.what == CHECK_FAIL) { + handlePreFailTask(msg.arg1); + return true; + } + if (msg.arg1 == IS_SUB_TASK) { return handleSubEvent(msg); } @@ -180,11 +184,11 @@ public class TaskSchedulers implements ISchedulers { } TASK task = (TASK) msg.obj; - if (task == null && msg.what != ISchedulers.CHECK_FAIL) { + if (task == null) { ALog.e(TAG, "请传入下载任务"); return true; } - handleNormalEvent(task, msg.what, msg.arg1); + handleNormalEvent(task, msg.what); return true; } @@ -292,8 +296,8 @@ public class TaskSchedulers implements ISchedulers { /** * 处理普通任务和任务组的事件 */ - private void handleNormalEvent(TASK task, int what, int arg1) { - ITaskQueue queue = getQueue(task); + private void handleNormalEvent(TASK task, int what) { + ITaskQueue queue = getQueue(task.getTaskType()); switch (what) { case STOP: if (task.getState() == IEntity.STATE_WAIT) { @@ -324,9 +328,6 @@ public class TaskSchedulers implements ISchedulers { case FAIL: handleFailTask(queue, task); break; - case CHECK_FAIL: - handlePreFailTask(queue, arg1); - break; } if (what == FAIL || what == CHECK_FAIL) { @@ -343,8 +344,13 @@ public class TaskSchedulers implements ISchedulers { normalTaskCallback(what, task); } - private void handlePreFailTask(ITaskQueue queue, int taskType) { - startNextTask(queue, TaskSchedulerType.TYPE_DEFAULT); + /** + * 处理what为{@link #CHECK_FAIL}信息错误的任务 + * + * @param taskType 任务类型 + */ + private void handlePreFailTask(int taskType) { + startNextTask(getQueue(taskType), TaskSchedulerType.TYPE_DEFAULT); // 发送广播 boolean canSend = mAriaManager.getAppConfig().isUseBroadcast(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java index 923a1fe1..2f0aff65 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.upload; import android.text.TextUtils; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; @@ -38,7 +39,7 @@ public class CheckUEntityUtil implements ICheckEntityUtil { @Override public boolean checkEntity() { if (mWrapper.getErrorEvent() != null) { - ALog.e(TAG, mWrapper.getErrorEvent().errorMsg); + ALog.e(TAG, String.format("上传失败,%s", mWrapper.getErrorEvent().errorMsg)); return false; } @@ -77,7 +78,7 @@ public class CheckUEntityUtil implements ICheckEntityUtil { if (TextUtils.isEmpty(url)) { ALog.e(TAG, "上传失败,url为null"); return false; - } else if (!CheckUtil.checkUrlNotThrow(url)) { + } else if (!CheckUtil.checkUrl(url)) { ALog.e(TAG, "上传失败,url【" + url + "】错误"); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index f0cc0ff9..fc119c45 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -70,7 +70,7 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public HttpBuilderTarget load(@NonNull String filePath) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkUploadPath(filePath); + CheckUtil.checkUploadPathIsEmpty(filePath); return UTargetFactory.getInstance() .generateBuilderTarget(HttpBuilderTarget.class, filePath); } @@ -84,7 +84,6 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public HttpNormalTarget load(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); - CheckUtil.checkTaskId(taskId); return UTargetFactory.getInstance() .generateNormalTarget(HttpNormalTarget.class, taskId); } @@ -97,7 +96,7 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public FtpBuilderTarget loadFtp(@NonNull String filePath) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkUploadPath(filePath); + CheckUtil.checkUploadPathIsEmpty(filePath); return UTargetFactory.getInstance() .generateBuilderTarget(FtpBuilderTarget.class, filePath); } @@ -111,7 +110,6 @@ public class UploadReceiver extends AbsReceiver { @CheckResult public FtpNormalTarget loadFtp(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); - CheckUtil.checkTaskId(taskId); return UTargetFactory.getInstance() .generateNormalTarget(FtpNormalTarget.class, taskId); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java index e06e6b05..310c8703 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/UNormalConfigHandler.java @@ -15,19 +15,18 @@ */ package com.arialyy.aria.core.upload.target; -import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.AbsNormalTarget; +import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.inf.IConfigHandler; -import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.orm.DbEntity; -import com.arialyy.aria.util.CommonUtil; import java.io.File; /** @@ -35,7 +34,6 @@ import java.io.File; * 普通上传任务通用功能处理 */ class UNormalConfigHandler implements IConfigHandler { - private String TAG = "UNormalDelegate"; private UploadEntity mEntity; private TARGET mTarget; private UTaskWrapper mWrapper; @@ -47,9 +45,15 @@ class UNormalConfigHandler implements IConfigHandler { private void initTarget(long taskId) { mWrapper = TaskWrapperManager.getInstance().getNormalTaskWrapper(UTaskWrapper.class, taskId); - if (taskId != -1 && mWrapper.getEntity().getId() == -1) { - mWrapper.setErrorEvent(new ErrorEvent(taskId, String.format("没有id为%s的任务", taskId))); + // 判断已存在的任务 + if (mTarget instanceof AbsNormalTarget) { + if (taskId < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务id为空")); + } else if (mWrapper.getEntity().getId() < 0) { + mWrapper.setErrorEvent(new ErrorEvent(taskId, "任务信息不存在")); + } } + mEntity = mWrapper.getEntity(); mTarget.setTaskWrapper(mWrapper); getTaskWrapper().setTempUrl(mEntity.getUrl()); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java index 121f5e07..62cf07ec 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java @@ -296,7 +296,7 @@ public class HttpFileInfoThread implements Runnable { newUrl = uri.getHost() + newUrl; } - if (!CheckUtil.checkUrlNotThrow(newUrl)) { + if (!CheckUtil.checkUrl(newUrl)) { failDownload(new TaskException(TAG, "下载失败,重定向url错误"), false); return; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java index 0aa9b98c..d4e637be 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java @@ -294,7 +294,7 @@ final public class M3U8InfoThread implements Runnable { newUrl = uri.getHost() + newUrl; } - if (!CheckUtil.checkUrlNotThrow(newUrl)) { + if (!CheckUtil.checkUrl(newUrl)) { failDownload("下载失败,重定向url错误", false); return; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java index 02aee8d8..9b0ea782 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/ErrorCode.java @@ -22,10 +22,11 @@ public enum ErrorCode { ERROR_CODE_URL_NULL(2, "url 为空"), ERROR_CODE_URL_INVALID(3, "url 无效"), ERROR_CODE_PAGE_NUM(4, "page和num不能小于1"), ERROR_CODE_GROUP_URL_NULL(5, "组合任务url列表为空"), ERROR_CODE_UPLOAD_FILE_NULL(7, "上传文件不存在"), - ERROR_CODE_MEMBER_WARNING(8, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); + ERROR_CODE_MEMBER_WARNING(8, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"), + ERROR_CODE_TASK_NOT_EXIST(9, "任务信息不存在"); - int code; - String msg; + public int code; + public String msg; ErrorCode(int code, String msg) { this.code = code; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java index e1d222ca..5adc4119 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/AbsGroupTaskWrapper.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.core.download; -import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import java.util.List; @@ -26,19 +25,6 @@ import java.util.List; public abstract class AbsGroupTaskWrapper extends AbsTaskWrapper { - /** - * 错误信息 - */ - private ErrorEvent errorEvent; - - public ErrorEvent getErrorEvent() { - return errorEvent; - } - - public void setErrorEvent(ErrorEvent errorEvent) { - this.errorEvent = errorEvent; - } - public AbsGroupTaskWrapper(ENTITY entity) { super(entity); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index 168cd70a..01190f61 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -80,7 +80,9 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { @Override public int getTaskType() { int type; - if (getUrl().startsWith("ftp")) { + if (TextUtils.isEmpty(getUrl())) { + type = ITaskWrapper.ERROR; + } else if (getUrl().startsWith("ftp")) { type = ITaskWrapper.D_FTP; } else { M3U8Entity temp = getM3U8Entity(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java index 329e1b3d..2b820db8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java @@ -62,7 +62,6 @@ public class DownloadGroupTask extends AbsGroupTask { Handler outHandler; public Builder(DGTaskWrapper taskEntity) { - CheckUtil.checkTaskEntity(taskEntity); this.taskEntity = taskEntity; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index 0966b12d..a2b283e9 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -17,7 +17,6 @@ package com.arialyy.aria.core.wrapper; import com.arialyy.aria.core.TaskOptionParams; import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.config.BaseTaskConfig; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; @@ -26,8 +25,6 @@ import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.util.ComponentUtil; -import java.util.ArrayList; -import java.util.List; /** * Created by lyy on 2017/2/23. 所有任务实体的父类 @@ -87,15 +84,6 @@ public abstract class AbsTaskWrapper */ private TaskOptionParams optionParams = new TaskOptionParams(); - /** - * 初始状态列表 - */ - private List errorCode = new ArrayList<>(); - - public List getErrorCode() { - return errorCode; - } - public void setTaskOption(ITaskOption option) { this.taskOption = option; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java index e2b628ff..047e6112 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java @@ -22,7 +22,7 @@ import com.arialyy.aria.core.inf.IEntity; */ public interface ITaskWrapper { - int DEFAULT = 0; + int ERROR = 0; /** * HTTP单任务载 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java index 614a35dc..be362bec 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/ActionPolicy.java @@ -46,7 +46,7 @@ public enum ActionPolicy { /** * 父表有变更时,子表将外键列设置成一个默认的值,default配置的值 */ - SET_DEFAULT("SET DEFAULT"), + SET_DEFAULT("SET ERROR"), /** * 在父表上update/delete记录时,同步update/delete掉子表的匹配记录 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java index 2fc2dcdd..8021eb22 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -184,7 +184,7 @@ class DelegateCommon extends AbsDelegate { if (SqlUtil.isDefault(field)) { Default d = field.getAnnotation(Default.class); if (!TextUtils.isEmpty(d.value())) { - sb.append(" DEFAULT ").append("'").append(d.value()).append("'"); + sb.append(" ERROR ").append("'").append(d.value()).append("'"); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index c479c525..34149011 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -17,13 +17,6 @@ package com.arialyy.aria.util; import android.text.TextUtils; -import com.arialyy.aria.core.common.ErrorCode; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import java.io.File; import java.lang.reflect.Modifier; import java.util.List; @@ -54,52 +47,12 @@ public class CheckUtil { if (page < 1 || num < 1) throw new NullPointerException("page和num不能小于1"); } - /** - * 检测url是否合法,如果url不合法,将抛异常 - */ - public static ErrorCode checkTaskId(long taskId) { - if (taskId < 0) { - return ErrorCode.ERROR_CODE_TASK_ID_NULL; - } - return ErrorCode.ERROR_CODE_NORMAL; - } - - /** - * 检测url是否合法,如果url不合法,将抛异常 - */ - public static void checkUrlInvalidThrow(String url) { - if (TextUtils.isEmpty(url)) { - throw new IllegalArgumentException("url不能为null"); - } else if (!url.startsWith("http") && !url.startsWith("ftp") && !url.startsWith("sftp")) { - throw new IllegalArgumentException("url错误"); - } - int index = url.indexOf("://"); - if (index == -1) { - throw new IllegalArgumentException("url不合法"); - } - } - - /** - * 检测url是否合法,如果url不合法,将抛出{@link IllegalArgumentException}异常 - */ - public static void checkUrl(String url) { - if (TextUtils.isEmpty(url)) { - throw new NullPointerException("url为空"); - } else if (!url.startsWith("http") && !url.startsWith("ftp") && !url.startsWith("sftp")) { - throw new IllegalArgumentException(String.format("url【%s】错误", url)); - } - int index = url.indexOf("://"); - if (index == -1) { - throw new IllegalArgumentException(String.format("url【%s】不合法", url)); - } - } - /** * 检测url是否合法 * * @return {@code true} 合法,{@code false} 非法 */ - public static boolean checkUrlNotThrow(String url) { + public static boolean checkUrl(String url) { if (TextUtils.isEmpty(url)) { ALog.e(TAG, "url不能为null"); return false; @@ -116,65 +69,23 @@ public class CheckUtil { /** * 检测下载链接组是否为null + * + * @return true 组合任务url为空 */ - public static void checkDownloadUrls(List urls) { + public static boolean checkDownloadUrlsIsEmpty(List urls) { if (urls == null || urls.isEmpty()) { - throw new IllegalArgumentException("链接组不能为null"); + ALog.e(TAG, "链接组不能为null"); + return true; } + return false; } /** * 检测上传地址是否为null */ - public static void checkUploadPath(String uploadPath) { + public static void checkUploadPathIsEmpty(String uploadPath) { if (TextUtils.isEmpty(uploadPath)) { throw new IllegalArgumentException("上传地址不能为null"); } - File file = new File(uploadPath); - if (!file.exists()) { - throw new IllegalArgumentException("上传文件不存在"); - } - } - - /** - * 检查任务实体 - */ - public static void checkTaskEntity(AbsTaskWrapper entity) { - if (entity instanceof DTaskWrapper) { - checkDownloadTaskEntity(((DTaskWrapper) entity).getEntity()); - } else if (entity instanceof UTaskWrapper) { - checkUploadTaskEntity(((UTaskWrapper) entity).getEntity()); - } - } - - /** - * 检查上传实体是否合法 - */ - private static void checkUploadTaskEntity(UploadEntity entity) { - if (entity == null) { - throw new NullPointerException("上传实体不能为空"); - } else if (TextUtils.isEmpty(entity.getFilePath())) { - throw new IllegalArgumentException("上传文件路径不能为空"); - } else if (TextUtils.isEmpty(entity.getFileName())) { - throw new IllegalArgumentException("上传文件名不能为空"); - } - } - - /** - * 检测下载实体是否合法 - * 合法(true) - * - * @param entity 下载实体 - */ - private static void checkDownloadTaskEntity(DownloadEntity entity) { - if (entity == null) { - throw new NullPointerException("下载实体不能为空"); - } else if (TextUtils.isEmpty(entity.getUrl())) { - throw new IllegalArgumentException("下载链接不能为空"); - } else if (TextUtils.isEmpty(entity.getFileName())) { - throw new NullPointerException("文件名不能为null"); - } else if (TextUtils.isEmpty(entity.getDownloadPath())) { - throw new NullPointerException("文件保存路径不能为null"); - } } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index 0f354836..16881fc4 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -29,6 +29,7 @@ import androidx.lifecycle.ViewModelProviders; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.arialyy.aria.core.AriaManager; import com.arialyy.frame.permission.OnPermissionCallback; import com.arialyy.frame.permission.PermissionManager; import com.arialyy.frame.util.show.T; diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index c33235a0..6522cdb6 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -93,10 +93,10 @@ public class FtpDownloadActivity extends BaseActivity mTaskId = Aria.download(this) .load(DOWNLOAD_URL) .setFilePath( - Environment.getExternalStorageDirectory().getPath() + String.format("%s.apk", + Environment.getExternalStorageDirectory().getPath() + String.format("/%s.apk", FILE_NAME)) .create(); getBinding().setStateStr(getString(R.string.stop)); diff --git a/app/src/main/java/com/arialyy/simple/core/download/fragment/FragmentActivity.java b/app/src/main/java/com/arialyy/simple/core/download/fragment/FragmentActivity.java index 483e0fb8..a3629fb0 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/fragment/FragmentActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/fragment/FragmentActivity.java @@ -38,6 +38,5 @@ public class FragmentActivity extends BaseActivity { final NormalTo to = getIntent().getParcelableExtra(MainActivity.KEY_MAIN_DATA); setTitle(to.title); - Aria.download(this).register(); } } From 0822c053cba6e0f87c557510d5e182db6fa36958 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 23 Oct 2019 22:29:39 +0800 Subject: [PATCH 010/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dm3u8=E7=82=B9?= =?UTF-8?q?=E6=92=AD=E6=97=A0=E6=B3=95=E8=AE=BE=E7=BD=AEtsurl=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E5=99=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/common/AbsBuilderTarget.java | 4 +-- .../core/download/m3u8/M3U8LiveOption.java | 2 +- .../aria/core/download/m3u8/M3U8Option.java | 35 ++++++------------- .../core/download/m3u8/M3U8VodOption.java | 17 ++++++++- .../download/target/FtpBuilderTarget.java | 3 +- .../core/download/FtpDownloadActivity.java | 6 ++-- .../download/m3u8/M3U8VodDLoadActivity.java | 2 +- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java index beb66c9b..c130894b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java @@ -37,7 +37,7 @@ public abstract class AbsBuilderTarget extends /** * 添加任务 * - * @return 正常添加,返回任务id,否则返回-1 + * @return 添加成功,返回任务id,创建失败,返回-1 */ @Override public long add() { @@ -47,7 +47,7 @@ public abstract class AbsBuilderTarget extends /** * 开始任务 * - * @return 正常启动,返回任务id,否则返回-1 + * @return 创建成功,返回任务id,创建失败,返回-1 */ @Override public long create() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java index 6d5d000a..c8a3b8f3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8LiveOption.java @@ -22,7 +22,7 @@ import com.arialyy.aria.util.CheckUtil; /** * m3u8直播参数设置 */ -public class M3U8LiveOption extends M3U8Option { +public class M3U8LiveOption extends M3U8Option { private ILiveTsUrlConverter liveTsUrlConverter; private long liveUpdateInterval; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java index fcd1b8b7..8dd23fc8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -25,13 +25,12 @@ import com.arialyy.aria.util.ComponentUtil; /** * m3u8任务设置 */ -public class M3U8Option extends BaseOption { +public class M3U8Option extends BaseOption { private boolean generateIndexFileTemp = false; private boolean mergeFile = false; private int bandWidth; private ITsMergeHandler mergeHandler; - private IVodTsUrlConverter vodUrlConverter; private IBandWidthUrlConverter bandWidthUrlConverter; M3U8Option() { @@ -43,9 +42,9 @@ public class M3U8Option extends BaseOption { * 生成m3u8索引文件 * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 */ - public M3U8Option generateIndexFile() { + public OP generateIndexFile() { this.generateIndexFileTemp = true; - return this; + return (OP) this; } /** @@ -53,31 +52,19 @@ public class M3U8Option extends BaseOption { * * @param mergeFile {@code true}合并所有ts文件为一个 */ - public M3U8Option merge(boolean mergeFile) { + public OP merge(boolean mergeFile) { this.mergeFile = mergeFile; - return this; + return (OP) this; } /** * 如果你希望使用自行处理ts文件的合并,可以使用{@link ITsMergeHandler}处理ts文件的合并 * 需要注意的是:只有{@link #merge(boolean)}设置合并ts文件,该方法才会生效 */ - public M3U8Option setMergeHandler(ITsMergeHandler mergeHandler) { + public OP setMergeHandler(ITsMergeHandler mergeHandler) { CheckUtil.checkMemberClass(mergeHandler.getClass()); this.mergeHandler = mergeHandler; - return this; - } - - /** - * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 - * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 - * - * @param vodUrlConverter {@link IVodTsUrlConverter} - */ - public M3U8Option setTsUrlConvert(IVodTsUrlConverter vodUrlConverter) { - CheckUtil.checkMemberClass(vodUrlConverter.getClass()); - this.vodUrlConverter = vodUrlConverter; - return this; + return (OP) this; } /** @@ -85,9 +72,9 @@ public class M3U8Option extends BaseOption { * * @param bandWidth 指定的码率 */ - public M3U8Option setBandWidth(int bandWidth) { + public OP setBandWidth(int bandWidth) { this.bandWidth = bandWidth; - return this; + return (OP) this; } /** @@ -96,9 +83,9 @@ public class M3U8Option extends BaseOption { * * @param bandWidthUrlConverter {@link IBandWidthUrlConverter} */ - public M3U8Option setBandWidthUrlConverter(IBandWidthUrlConverter bandWidthUrlConverter) { + public OP setBandWidthUrlConverter(IBandWidthUrlConverter bandWidthUrlConverter) { CheckUtil.checkMemberClass(bandWidthUrlConverter.getClass()); this.bandWidthUrlConverter = bandWidthUrlConverter; - return this; + return (OP) this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java index 039009e6..563224c7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8VodOption.java @@ -15,20 +15,35 @@ */ package com.arialyy.aria.core.download.m3u8; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; /** * m3u8点播文件参数设置 */ -public class M3U8VodOption extends M3U8Option { +public class M3U8VodOption extends M3U8Option { private long fileSize; private int maxTsQueueNum; private int jumpIndex; + private IVodTsUrlConverter vodUrlConverter; public M3U8VodOption() { super(); } + /** + * M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的 + * 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址 + * + * @param vodUrlConverter {@link IVodTsUrlConverter} + */ + public M3U8VodOption setVodTsUrlConvert(IVodTsUrlConverter vodUrlConverter) { + CheckUtil.checkMemberClass(vodUrlConverter.getClass()); + this.vodUrlConverter = vodUrlConverter; + return this; + } + /** * 由于m3u8协议的特殊性质,无法有效快速获取到正确到文件长度,如果你需要显示文件中长度,你需要自行设置文件长度 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 0baadabe..6bb56047 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -39,8 +39,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @com.arialyy.aria.core.common.CheckResult(suggest = Suggest.TASK_CONTROLLER) - //@CheckResult(suggest = Suggest.TASK_CONTROLLER) + @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 6522cdb6..f1c4cef1 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -93,10 +93,10 @@ public class FtpDownloadActivity extends BaseActivity { M3U8VodOption option = new M3U8VodOption(); option .generateIndexFile() - .setTsUrlConvert(new VodTsUrlConverter()) + .setVodTsUrlConvert(new VodTsUrlConverter()) .setMergeHandler(new TsMergeHandler()); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; From a575e885a9cb7937f4eda47c57f384534c1d0c96 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sun, 27 Oct 2019 01:28:34 +0800 Subject: [PATCH 011/140] 3.7.1 --- .../core/upload/target/HttpNormalTarget.java | 5 +++ DEV_LOG.md | 4 ++ .../aria/core/loader/NormalLoader.java | 2 +- README.md | 45 +++++++++++++------ build.gradle | 2 +- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index 2b7a3d5d..27a6e1fa 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -20,6 +20,7 @@ import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ALog; /** * Created by lyy on 2017/2/28. @@ -57,6 +58,10 @@ public class HttpNormalTarget extends AbsNormalTarget { return this; } + @Override public void resume() { + ALog.e(TAG, "http上传任务没有恢复功能"); + } + @Override public boolean isRunning() { return mConfigHandler.isRunning(); } diff --git a/DEV_LOG.md b/DEV_LOG.md index a5ede232..a8bc96af 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -5,6 +5,10 @@ - fix bug https://github.com/AriaLyy/Aria/issues/454 - fix bug https://github.com/AriaLyy/Aria/issues/467 - fix bug https://github.com/AriaLyy/Aria/issues/459 + - fix bug https://github.com/AriaLyy/Aria/issues/487 + - fix bug https://github.com/AriaLyy/Aria/issues/483 + - fix bug https://github.com/AriaLyy/Aria/issues/482 + - fix bug https://github.com/AriaLyy/Aria/issues/473 - 移除隐藏api的反射 https://github.com/AriaLyy/Aria/issues/456 - 新增ftp免证书登陆功能h ttps://github.com/AriaLyy/Aria/issues/455 - 适配androidX diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index 9221a312..1fd67739 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -99,7 +99,7 @@ public class NormalLoader extends AbsLoader { ((IDLoadListener) mListener).onPostPre(getEntity().getFileSize()); } File file = new File(getEntity().getFilePath()); - if (!file.getParentFile().exists()) { + if (file.getParentFile() != null && !file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } diff --git a/README.md b/README.md index 2143013f..c74f2625 100644 --- a/README.md +++ b/README.md @@ -43,23 +43,30 @@ Aria有以下特点: ![m3u8点播文件边下边看](https://github.com/AriaLyy/Aria/blob/master/img/m3u8VodDownload.gif) ## 引入库 -[![Core](https://api.bintray.com/packages/arialyy/maven/AriaApi/images/download.svg)](https://bintray.com/arialyy/maven/AriaApi/_latestVersion) -[![Compiler](https://api.bintray.com/packages/arialyy/maven/AriaCompiler/images/download.svg)](https://bintray.com/arialyy/maven/AriaCompiler/_latestVersion) +[![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) +[![Core](https://img.shields.io/badge/Core-3.7.1-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.1-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.1-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.1-orange)](https://github.com/AriaLyy/Aria) ```java -compile 'com.arialyy.aria:aria-core:3.6.6' -annotationProcessor 'com.arialyy.aria:aria-compiler:3.6.6' +implementation 'com.arialyy.aria:core:3.7.1' +annotationProcessor 'com.arialyy.aria:compiler:3.7.1' +implementation 'com.arialyy.aria:ftpComponent:3.7.1' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.1' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 ``` api('com.arialyy.aria:aria-core:'){ - exclude group: 'com.android.support' + exclude group: 'androidx.appcompat.app' } ``` 如果你使用的是kotlin,请使用kotlin官方提供的方法配置apt,[kotlin kapt官方配置传送门](https://www.kotlincn.net/docs/reference/kapt.html) __⚠️注意:3.5.4以下版本升级时,需要更新[配置文件](https://aria.laoyuyu.me/aria_doc/start/config.html)!!__ +__⚠️注意:3.7 以上版本已经适配了AndroidX__ + *** ## 使用 由于Aria涉及到文件和网络的操作,因此需要你在manifest文件中添加以下权限,如果你希望在6.0以上的系统中使用Aria,那么你需要动态向安卓系统申请文件系统读写权限,[如何使用安卓系统权限](https://developer.android.com/training/permissions/index.html?hl=zh-cn) @@ -126,17 +133,27 @@ protected void onCreate(Bundle savedInstanceState) { } ``` +### [文档地址](https://aria.laoyuyu.me/aria_doc/) + ### 版本日志 - + v_3.6.6 - - fix bug https://github.com/AriaLyy/Aria/issues/426 - - fix bug https://github.com/AriaLyy/Aria/issues/429 - - fix bug https://github.com/AriaLyy/Aria/issues/428 - - fix bug https://github.com/AriaLyy/Aria/issues/427 - - fix bug https://github.com/AriaLyy/Aria/issues/431 - - fix bug https://github.com/AriaLyy/Aria/issues/441 - - 修复普通下载任务、组合任务共享执行队列、缓存池的问题 - - 修复组合任务启动失败时,`DownloadGroupEntity`的状态变为执行中的问题 + + v_3.7 + - fix bug https://github.com/AriaLyy/Aria/issues/450 + - fix bug https://github.com/AriaLyy/Aria/issues/466 + - fix bug https://github.com/AriaLyy/Aria/issues/454 + - fix bug https://github.com/AriaLyy/Aria/issues/467 + - fix bug https://github.com/AriaLyy/Aria/issues/459 + - fix bug https://github.com/AriaLyy/Aria/issues/487 + - fix bug https://github.com/AriaLyy/Aria/issues/483 + - fix bug https://github.com/AriaLyy/Aria/issues/482 + - fix bug https://github.com/AriaLyy/Aria/issues/473 + - 移除隐藏api的反射 https://github.com/AriaLyy/Aria/issues/456 + - 新增ftp免证书登陆功能h ttps://github.com/AriaLyy/Aria/issues/455 + - 适配androidX + - 修复组合任务,恢复下载,会出现进度显示为0的问题 + - m3u8点播下载新增创建ts索引功能 + - 修复多任务的m3u8点播下载时,一个任务调用`jumpIndex`,其它m3u8任务也会自动调用`jumpIndex`的问题 + - 添加权限检查 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/build.gradle b/build.gradle index 1f82c71c..a5bb8b66 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ task clean(type: Delete) { ext { versionCode = 371 - versionName = '3.7_pre_2' + versionName = '3.7.1' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 39c485b15716521c95842d0ee70676d737b9be9e Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 28 Oct 2019 21:52:46 +0800 Subject: [PATCH 012/140] 3.7.2 --- .../com/arialyy/aria/http/HttpTaskOption.java | 5 ++-- .../com/arialyy/aria/m3u8/M3U8TaskOption.java | 4 +-- .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 2 +- .../arialyy/aria/m3u8/vod/M3U8VodUtil.java | 2 +- .../arialyy/aria/core/TaskOptionParams.java | 26 +++++++++++++++++-- .../com/arialyy/aria/util/ComponentUtil.java | 3 ++- README.md | 16 ++++++------ .../download/m3u8/M3U8VodDLoadActivity.java | 2 -- .../download/m3u8/VideoPlayerFragment.java | 11 +++++++- build.gradle | 4 +-- 10 files changed, 53 insertions(+), 22 deletions(-) diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java index d013f42e..fb898c70 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpTaskOption.java @@ -16,9 +16,10 @@ package com.arialyy.aria.http; +import android.text.TextUtils; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.inf.ITaskOption; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import java.lang.ref.SoftReference; import java.net.CookieManager; import java.net.Proxy; @@ -140,7 +141,7 @@ public class HttpTaskOption implements ITaskOption { } public String getCharSet() { - return charSet; + return TextUtils.isEmpty(charSet) ? "utf-8" : charSet; } public void setCharSet(String charSet) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index ba680258..591cbf87 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -120,7 +120,7 @@ public class M3U8TaskOption implements ITaskOption { } public int getMaxTsQueueNum() { - return maxTsQueueNum; + return maxTsQueueNum == 0 ? 4 : maxTsQueueNum; } public void setMaxTsQueueNum(int maxTsQueueNum) { @@ -128,7 +128,7 @@ public class M3U8TaskOption implements ITaskOption { } public long getLiveUpdateInterval() { - return liveUpdateInterval; + return liveUpdateInterval == 0 ? 10 * 1000 : liveUpdateInterval; } public void setLiveUpdateInterval(long liveUpdateInterval) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index dc688518..5038c097 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -334,7 +334,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } /** - * 重指定位置恢复任务 + * 从指定位置恢复任务 */ private synchronized void resumeTask() { if (isBreak()) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java index 1a7a1ebe..46f82d51 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java @@ -82,7 +82,7 @@ public class M3U8VodUtil extends AbsNormalLoaderUtil { fail(new M3U8Exception(TAG, "获取地址失败"), false); return; } else if (!mUrls.get(0).startsWith("http")) { - fail(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); + fail(new M3U8Exception(TAG, "地址错误,请使用IVodTsUrlConverter处理你的url信息"), false); return; } mM3U8Option.setUrls(mUrls); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java index 66512522..4d3a0f59 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskOptionParams.java @@ -18,10 +18,19 @@ package com.arialyy.aria.core; import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; +import com.arialyy.aria.core.processor.IHttpFileLenAdapter; +import com.arialyy.aria.core.processor.ILiveTsUrlConverter; +import com.arialyy.aria.core.processor.ITsMergeHandler; +import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -32,6 +41,8 @@ import java.util.Map; */ public class TaskOptionParams { + private static List PROCESSORES = new ArrayList<>(); + /** * 普通参数 */ @@ -42,18 +53,29 @@ public class TaskOptionParams { */ private Map handler = new HashMap<>(); + static { + PROCESSORES.add(FtpInterceptHandler.class); + PROCESSORES.add(IBandWidthUrlConverter.class); + PROCESSORES.add(IFtpUploadInterceptor.class); + PROCESSORES.add(IHttpFileLenAdapter.class); + PROCESSORES.add(ILiveTsUrlConverter.class); + PROCESSORES.add(ITsMergeHandler.class); + PROCESSORES.add(IVodTsUrlConverter.class); + } + /** * 设置任务参数 * * @param option 任务配置 */ public void setParams(BaseOption option) { - Field[] fields = CommonUtil.getFields(option.getClass()); + List fields = CommonUtil.getAllFields(option.getClass()); for (Field field : fields) { field.setAccessible(true); try { - if (field.getType() == IEventHandler.class) { + + if (PROCESSORES.contains(field.getType())) { Object eventHandler = field.get(option); if (eventHandler != null) { setObjs(field.getName(), (IEventHandler) eventHandler); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 600e6ae1..5ea85006 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -28,6 +28,7 @@ import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.util.List; /** * 组件工具,用于跨组件创建对应的工具类 @@ -210,7 +211,7 @@ public class ComponentUtil { * @return 构建失败,返回null */ public T buildTaskOption(Class clazz, TaskOptionParams params) { - Field[] fields = CommonUtil.getFields(clazz); + List fields = CommonUtil.getAllFields(clazz); T taskOption = null; try { taskOption = clazz.newInstance(); diff --git a/README.md b/README.md index c74f2625..1bc30f4c 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.1-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.1-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.1-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.1-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.2-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.2-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.2-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.2-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.1' -annotationProcessor 'com.arialyy.aria:compiler:3.7.1' -implementation 'com.arialyy.aria:ftpComponent:3.7.1' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.1' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.2' +annotationProcessor 'com.arialyy.aria:compiler:3.7.2' +implementation 'com.arialyy.aria:ftpComponent:3.7.2' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.2' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 ``` diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index dd44584f..f04b6041 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -310,8 +310,6 @@ public class M3U8VodDLoadActivity extends BaseActivity { return option; } - private Class c = BuilderController.class; - @Override protected void dataCallback(int result, Object data) { super.dataCallback(result, data); if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) { diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java index 501da945..0d281500 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/VideoPlayerFragment.java @@ -1,6 +1,8 @@ package com.arialyy.simple.core.download.m3u8; import android.annotation.SuppressLint; +import android.graphics.Canvas; +import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; @@ -37,6 +39,7 @@ public class VideoPlayerFragment extends BaseFragment Date: Wed, 30 Oct 2019 10:13:21 +0800 Subject: [PATCH 013/140] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bc30f4c..a77a7454 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ annotationProcessor 'com.arialyy.aria:compiler:3.7.2' implementation 'com.arialyy.aria:ftpComponent:3.7.2' # 如果需要使用ftp,请增加该组件 implementation 'com.arialyy.aria:m3u8Component:3.7.2' # 如果需要使用m3u8下载功能,请增加该组件 ``` -如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 +如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` api('com.arialyy.aria:aria-core:'){ exclude group: 'androidx.appcompat.app' From d6993eb1598bec8d750b00e6b146c3b012f17ed6 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Fri, 1 Nov 2019 21:20:07 +0800 Subject: [PATCH 014/140] fix bug https://github.com/AriaLyy/Aria/issues/495 fix bug https://github.com/AriaLyy/Aria/issues/496 --- .../com/arialyy/aria/core/AriaManager.java | 2 +- DEV_LOG.md | 7 +++- .../com/arialyy/aria/core/AriaConfig.java | 2 ++ .../aria/core/manager/ThreadTaskManager.java | 2 +- .../java/com/arialyy/aria/orm/SqlUtil.java | 4 ++- .../com/arialyy/aria/util/CommonUtil.java | 10 ++++++ README.md | 36 ++++++------------- .../core/download/HttpDownloadModule.java | 3 +- .../core/download/SingleTaskActivity.java | 4 +-- .../core/download/mutil/DownloadAdapter.java | 24 ++++++++++++- .../download/mutil/MultiDownloadActivity.java | 3 ++ .../download/mutil/MultiTaskActivity.java | 3 ++ build.gradle | 4 +-- 13 files changed, 69 insertions(+), 35 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index dc988c96..be47804d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -401,7 +401,7 @@ import java.util.concurrent.ConcurrentHashMap; return false; } else { String parentName = parentClass.getName(); - if (mFragmentClassName.contains(parentName)) { + if (mDialogFragmentClassName.contains(parentName)) { return true; } else { return isDialogFragment(parentClass); diff --git a/DEV_LOG.md b/DEV_LOG.md index a8bc96af..627fe4bb 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,5 +1,10 @@ ## 开发日志 - + v_3.7 + + v_3.7.4 + - 修复一个class被莫名改变的问题 + + v_3.7.3 (2019/10/31) + - fix bug https://github.com/AriaLyy/Aria/issues/495 + - fix bug https://github.com/AriaLyy/Aria/issues/496 + + v_3.7.2 (2019/10/28) - fix bug https://github.com/AriaLyy/Aria/issues/450 - fix bug https://github.com/AriaLyy/Aria/issues/466 - fix bug https://github.com/AriaLyy/Aria/issues/454 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java index 3676ca51..cfe73566 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java @@ -43,6 +43,8 @@ public class AriaConfig { public static final String DOWNLOAD_TEMP_DIR = "/Aria/temp/download/"; public static final String UPLOAD_TEMP_DIR = "/Aria/temp/upload/"; + public static final String IGNORE_CLASS_KLASS = "shadow$_klass_"; + public static final String IGNORE_CLASS_MONITOR = "shadow$_monitor_"; private static volatile AriaConfig Instance; private static Context APP; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java index 75a28cbd..24b83aa1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java @@ -48,7 +48,7 @@ public class ThreadTaskManager { } private ThreadTaskManager() { - mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + mExePool = Executors.newCachedThreadPool(); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java index 5e9116a7..0dea4c5d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.orm; import android.text.TextUtils; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.orm.annotation.Default; import com.arialyy.aria.orm.annotation.Foreign; import com.arialyy.aria.orm.annotation.Ignore; @@ -178,7 +179,8 @@ final class SqlUtil { int modifiers = field.getModifiers(); String fieldName = field.getName(); return (ignore != null && ignore.value()) || fieldName.equals("rowID") || fieldName.equals( - "shadow$_klass_") || fieldName.equals("shadow$_monitor_") || field.isSynthetic() || Modifier + AriaConfig.IGNORE_CLASS_KLASS) || fieldName.equals(AriaConfig.IGNORE_CLASS_MONITOR) + || field.isSynthetic() || Modifier .isStatic(modifiers) || Modifier.isFinal(modifiers); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index cbf97a57..07eb8088 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -626,6 +626,16 @@ public class CommonUtil { Collections.addAll(fields, personClazz.getDeclaredFields()); } Collections.addAll(fields, clazz.getDeclaredFields()); + List ignore = new ArrayList<>(); + for (Field field : fields) { + if (field.getName().equals(AriaConfig.IGNORE_CLASS_KLASS) || field.getName() + .equals(AriaConfig.IGNORE_CLASS_MONITOR)) { + ignore.add(field); + } + } + if (!ignore.isEmpty()){ + fields.removeAll(ignore); + } return fields; } diff --git a/README.md b/README.md index 1bc30f4c..5bb590a1 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.2-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.2-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.2-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.2-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.3-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.3-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.3-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.3-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.2' -annotationProcessor 'com.arialyy.aria:compiler:3.7.2' -implementation 'com.arialyy.aria:ftpComponent:3.7.2' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.2' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.3' +annotationProcessor 'com.arialyy.aria:compiler:3.7.3' +implementation 'com.arialyy.aria:ftpComponent:3.7.3' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.3' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:aria-core:'`替换为 ``` @@ -137,23 +137,9 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7 - - fix bug https://github.com/AriaLyy/Aria/issues/450 - - fix bug https://github.com/AriaLyy/Aria/issues/466 - - fix bug https://github.com/AriaLyy/Aria/issues/454 - - fix bug https://github.com/AriaLyy/Aria/issues/467 - - fix bug https://github.com/AriaLyy/Aria/issues/459 - - fix bug https://github.com/AriaLyy/Aria/issues/487 - - fix bug https://github.com/AriaLyy/Aria/issues/483 - - fix bug https://github.com/AriaLyy/Aria/issues/482 - - fix bug https://github.com/AriaLyy/Aria/issues/473 - - 移除隐藏api的反射 https://github.com/AriaLyy/Aria/issues/456 - - 新增ftp免证书登陆功能h ttps://github.com/AriaLyy/Aria/issues/455 - - 适配androidX - - 修复组合任务,恢复下载,会出现进度显示为0的问题 - - m3u8点播下载新增创建ts索引功能 - - 修复多任务的m3u8点播下载时,一个任务调用`jumpIndex`,其它m3u8任务也会自动调用`jumpIndex`的问题 - - 添加权限检查 + + v_3.7.3 + - fix bug https://github.com/AriaLyy/Aria/issues/495 + - fix bug https://github.com/AriaLyy/Aria/issues/496 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index dd449cb9..19bfc6db 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -47,9 +47,10 @@ public class HttpDownloadModule extends BaseViewModule { * 单任务下载的信息 */ LiveData getHttpDownloadInfo(Context context) { - String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl); + //String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl); //String url = // "http://sdkdown.muzhiwan.com/openfile/2019/05/21/com.netease.tom.mzw_5ce3ef8754d05.apk"; + String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 1bc32a7f..64c0b12c 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -289,8 +289,8 @@ public class SingleTaskActivity extends BaseActivity { private void startD() { HttpOption option = new HttpOption(); option.addHeader("1", "@") - .setFileLenAdapter(new FileLenAdapter()) - .useServerFileName(true); + .useServerFileName(true) + .setFileLenAdapter(new FileLenAdapter()); mTaskId = Aria.download(SingleTaskActivity.this) .load(mUrl) .setFilePath(mFilePath, true) diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java index f59fb5d6..ef05d29a 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java @@ -257,7 +257,11 @@ public class DownloadAdapter extends AbsRVAdapter { } @Download.onTaskFail void taskFail(DownloadTask task) { + if (task == null || task.getEntity() == null){ + return; + } mAdapter.updateBtState(task.getKey(), true); } diff --git a/build.gradle b/build.gradle index 5591b15f..69b9f7bf 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 372 - versionName = '3.7.2' + versionCode = 373 + versionName = '3.7.3' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From c1b1cc139050dfd0f7862c8e3c22c290bcb33550 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sat, 2 Nov 2019 10:58:49 +0800 Subject: [PATCH 015/140] fix bug https://github.com/AriaLyy/Aria/issues/493 --- FtpComponent/build.gradle | 1 - .../arialyy/aria/ftp/AbsFtpInfoThread.java | 2 +- .../arialyy/aria/ftp/FtpRecordAdapter.java | 13 +- FtpComponent/src/main/res/values/strings.xml | 3 - .../arialyy/aria/http/HttpRecordAdapter.java | 13 +- .../http/download/HttpDLoaderAdapter.java | 6 +- .../http/download/HttpDThreadTaskAdapter.java | 6 +- HttpComponent/src/main/res/values/strings.xml | 3 - .../com/arialyy/aria/m3u8/M3U8InfoThread.java | 2 +- .../aria/m3u8/live/M3U8LiveLoader.java | 2 +- .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 2 +- M3U8Component/src/main/res/values/strings.xml | 3 - .../aria/core/common/RecordHandler.java | 4 +- .../aria/core/common/RecordHelper.java | 41 ++++++ .../arialyy/aria/core/task/ThreadTask.java | 4 +- .../java/com/arialyy/aria/util/ErrorHelp.java | 2 +- .../java/com/arialyy/aria/util/FileUtil.java | 25 ++-- .../src/main/res/values/strings.xml | 3 - SFtpComponent/.gitignore | 1 + SFtpComponent/build.gradle | 33 +++++ SFtpComponent/consumer-rules.pro | 0 SFtpComponent/proguard-rules.pro | 21 +++ SFtpComponent/src/main/AndroidManifest.xml | 2 + .../arialyy/aria/sftp/AbsSFtpInfoThread.java | 58 ++++++++ .../java/com/arialyy/aria/sftp/SFtpLogin.java | 125 ++++++++++++++++++ app/src/main/assets/aria_config.xml | 2 +- .../core/download/HttpDownloadModule.java | 2 +- .../java/com/arialyy/simple/util/AppUtil.java | 2 +- settings.gradle | 2 +- 29 files changed, 331 insertions(+), 52 deletions(-) delete mode 100644 FtpComponent/src/main/res/values/strings.xml delete mode 100644 HttpComponent/src/main/res/values/strings.xml delete mode 100644 M3U8Component/src/main/res/values/strings.xml delete mode 100644 PublicComponent/src/main/res/values/strings.xml create mode 100644 SFtpComponent/.gitignore create mode 100644 SFtpComponent/build.gradle create mode 100644 SFtpComponent/consumer-rules.pro create mode 100644 SFtpComponent/proguard-rules.pro create mode 100644 SFtpComponent/src/main/AndroidManifest.xml create mode 100644 SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java create mode 100644 SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java diff --git a/FtpComponent/build.gradle b/FtpComponent/build.gradle index 7974c103..10b1fbb7 100644 --- a/FtpComponent/build.gradle +++ b/FtpComponent/build.gradle @@ -27,7 +27,6 @@ dependencies { implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" -// implementation project(path: ':AriaFtpPlug') implementation project(path: ':PublicComponent') } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java index 87f63342..468e6035 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java @@ -53,7 +53,7 @@ import javax.net.ssl.SSLContext; public abstract class AbsFtpInfoThread> implements Runnable { - private final String TAG = "AbsFtpInfoThread"; + private final String TAG = CommonUtil.getClassName(getClass()); protected ENTITY mEntity; protected TASK_WRAPPER mTaskWrapper; protected FtpTaskOption mTaskOption; diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java index 83227543..47debcd6 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java @@ -21,9 +21,8 @@ import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; import com.arialyy.aria.core.common.RecordHelper; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.common.AbsNormalEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.RecordUtil; import java.util.ArrayList; @@ -40,8 +39,12 @@ public class FtpRecordAdapter extends AbsRecordHandlerAdapter { @Override public void handlerTaskRecord(TaskRecord record) { RecordHelper helper = new RecordHelper(getWrapper(), record); - if (record.isBlock) { - helper.handleBlockRecord(); + if (getWrapper().isSupportBP()) { + if (record.isBlock) { + helper.handleBlockRecord(); + } else { + helper.handleMutilRecord(); + } } else if (record.threadNum == 1) { helper.handleSingleThreadRecord(); } @@ -104,6 +107,4 @@ public class FtpRecordAdapter extends AbsRecordHandlerAdapter { return 1; } } - - } diff --git a/FtpComponent/src/main/res/values/strings.xml b/FtpComponent/src/main/res/values/strings.xml deleted file mode 100644 index 08862ffd..00000000 --- a/FtpComponent/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - AriaFtpComponent - diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java index 311ec0ed..88daeb7f 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java @@ -38,15 +38,19 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { @Override public void onPre() { super.onPre(); - if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP){ + if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP) { RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD); } } @Override public void handlerTaskRecord(TaskRecord record) { RecordHelper helper = new RecordHelper(getWrapper(), record); - if (record.isBlock) { - helper.handleBlockRecord(); + if (getWrapper().isSupportBP()) { + if (record.isBlock) { + helper.handleBlockRecord(); + } else { + helper.handleMutilRecord(); + } } else if (!getWrapper().isSupportBP()) { helper.handleNoSupportBPRecord(); } else { @@ -81,7 +85,8 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { record.threadNum = threadNum; int requestType = getWrapper().getRequestType(); - if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) { + if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR + || requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) { record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); // 线程数为1,或者使用了分块,则认为是使用动态长度文件 record.isOpenDynamicFile = threadNum == 1 || record.isBlock; diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java index 442b9816..0b5ed415 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java @@ -28,6 +28,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.http.HttpRecordAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.FileUtil; import java.io.File; import java.io.IOException; @@ -43,16 +44,15 @@ final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter { @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { if (!record.isBlock) { if (getTempFile().exists()) { - getTempFile().delete(); + FileUtil.deleteFile(getTempFile()); } - //CommonUtil.createFile(mTempFile.getPath()); } else { for (int i = 0; i < totalThreadNum; i++) { File blockFile = new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); if (blockFile.exists()) { ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); - blockFile.delete(); + FileUtil.deleteFile(blockFile); } } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index bab8b614..e69ca84e 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -15,13 +15,13 @@ */ package com.arialyy.aria.http.download; +import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.http.BaseHttpThreadTaskAdapter; import com.arialyy.aria.http.ConnectionHelp; -import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; import java.io.BufferedInputStream; @@ -123,6 +123,10 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { fail(new TaskException(TAG, String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), getEntity().getFilePath(), getEntity().getUrl()), e), true); + } catch (ArrayIndexOutOfBoundsException e) { + fail(new TaskException(TAG, + String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), + getEntity().getFilePath(), getEntity().getUrl()), e), false); } catch (Exception e) { fail(new TaskException(TAG, String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), diff --git a/HttpComponent/src/main/res/values/strings.xml b/HttpComponent/src/main/res/values/strings.xml deleted file mode 100644 index c76105c3..00000000 --- a/HttpComponent/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - HttpComponent - diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java index d4e637be..45be0b66 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java @@ -355,7 +355,7 @@ final public class M3U8InfoThread implements Runnable { File keyF = new File(info.keyPath); if (!keyF.exists()) { ALog.d(TAG, "密钥不存在,下载密钥"); - FileUtil.createFile(keyF.getPath()); + FileUtil.createFile(keyF); } else { return; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index e7d7928d..297e5322 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -150,7 +150,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { config.stateHandler = mStateHandler; if (!config.tempFile.exists()) { - FileUtil.createFile(config.tempFile.getPath()); + FileUtil.createFile(config.tempFile); } ThreadTask threadTask = new ThreadTask(config); M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 5038c097..19211a57 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -434,7 +434,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { config.stateHandler = mStateHandler; config.peerIndex = index; if (!config.tempFile.exists()) { - FileUtil.createFile(config.tempFile.getPath()); + FileUtil.createFile(config.tempFile); } ThreadTask threadTask = new ThreadTask(config); M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config); diff --git a/M3U8Component/src/main/res/values/strings.xml b/M3U8Component/src/main/res/values/strings.xml deleted file mode 100644 index bd8f4556..00000000 --- a/M3U8Component/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - M3U8Component - diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index 490715bb..bbdf78bf 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -86,8 +86,8 @@ public class RecordHandler implements IRecordHandler { mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); initRecord(false); } - mAdapter.handlerTaskRecord(mTaskRecord); } + mAdapter.handlerTaskRecord(mTaskRecord); } saveRecord(); return mTaskRecord; @@ -145,7 +145,7 @@ public class RecordHandler implements IRecordHandler { } mTaskRecord.threadRecords.add(tRecord); } - mConfigFile.delete(); + FileUtil.deleteFile(mConfigFile); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index a3edd328..a6d8d157 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -20,7 +20,10 @@ import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.FileUtil; import java.io.File; +import java.io.IOException; /** * 任务记录帮助类,用于处理统一的逻辑 @@ -39,6 +42,44 @@ public class RecordHelper { mTaskRecord = record; } + /** + * 处理非分块的,多线程任务 + */ + public void handleMutilRecord() { + // 默认线程分块长度 + long blockSize = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size(); + File temp = new File(mTaskRecord.filePath); + boolean fileExists = false; + if (!temp.exists()) { + BufferedRandomAccessFile tempFile = null; + try { + tempFile = new BufferedRandomAccessFile(temp, "rw"); + tempFile.setLength(mWrapper.getEntity().getFileSize()); + } catch (IOException e) { + e.printStackTrace(); + } + + } else { + fileExists = true; + } + // 处理文件被删除的情况 + if (fileExists) { + for (int i = 0; i < mTaskRecord.threadNum; i++) { + long startL = i * blockSize, endL = (i + 1) * blockSize; + ThreadRecord tr = mTaskRecord.threadRecords.get(i); + tr.startLocation = startL; + tr.isComplete = false; + + //最后一个线程的结束位置即为文件的总长度 + if (tr.threadId == (mTaskRecord.threadNum - 1)) { + endL = mWrapper.getEntity().getFileSize(); + } + tr.endLocation = endL; + } + } + mWrapper.setNewTask(false); + } + /** * 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 464b9af6..23722c8b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -367,8 +367,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { || isNotNetRetry) && !isBreak()) { ALog.w(TAG, String.format("ts切片【%s】正在重试", getFileName())); mFailTimes++; - mConfig.tempFile.delete(); - FileUtil.createFile(mConfig.tempFile.getPath()); + FileUtil.deleteFile(mConfig.tempFile); + FileUtil.createFile(mConfig.tempFile); ThreadTaskManager.getInstance().retryThread(this); } else { sendFailMsg(null); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java index a89b933e..c0fb0f11 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java @@ -79,7 +79,7 @@ public class ErrorHelp { try { File file = new File(getLogPath()); if (!file.exists()) { - FileUtil.createFile(file.getPath()); + FileUtil.createFile(file); } writer = new PrintWriter(new FileWriter(file.getPath(), true)); writer.append(stringBuffer); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java index 424c4679..4b1593ba 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java @@ -86,22 +86,23 @@ public class FileUtil { ALog.e(TAG, "文件路径不能为null"); return false; } - File file = new File(path); + return createFile(new File(path)); + } + + /** + * 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件 + * + * @return {@code true} 创建成功、{@code false} 创建失败 + */ + public static boolean createFile(File file) { if (file.getParentFile() == null || !file.getParentFile().exists()) { ALog.d(TAG, "目标文件所在路径不存在,准备创建……"); if (!createDir(file.getParent())) { - ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + path + "】"); - } - } - // 创建目标文件 - if (file.exists()) { - final File to = new File(file.getAbsolutePath() + System.currentTimeMillis()); - if (file.renameTo(to)) { - to.delete(); - } else { - file.delete(); + ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + file.getPath() + "】"); } } + // 文件存在,删除文件 + deleteFile(file); try { if (file.createNewFile()) { //ALog.d(TAG, "创建文件成功:" + file.getAbsolutePath()); @@ -327,7 +328,7 @@ public class FileUtil { ALog.d(TAG, String.format("block = %s", block)); File subFile = new File(subPath); if (!subFile.exists()) { - subFile.createNewFile(); + createFile(subFile); } FileOutputStream fos = new FileOutputStream(subFile); FileChannel sfoc = fos.getChannel(); diff --git a/PublicComponent/src/main/res/values/strings.xml b/PublicComponent/src/main/res/values/strings.xml deleted file mode 100644 index 568a9247..00000000 --- a/PublicComponent/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - PublicComponent - diff --git a/SFtpComponent/.gitignore b/SFtpComponent/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/SFtpComponent/.gitignore @@ -0,0 +1 @@ +/build diff --git a/SFtpComponent/build.gradle b/SFtpComponent/build.gradle new file mode 100644 index 00000000..193b7be6 --- /dev/null +++ b/SFtpComponent/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" + implementation "com.jcraft:jsch:0.1.55" + implementation "com.jcraft:jzlib:1.1.3" + implementation project(path: ':FtpComponent') + compile project(path: ':PublicComponent') +} diff --git a/SFtpComponent/consumer-rules.pro b/SFtpComponent/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/SFtpComponent/proguard-rules.pro b/SFtpComponent/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/SFtpComponent/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/SFtpComponent/src/main/AndroidManifest.xml b/SFtpComponent/src/main/AndroidManifest.xml new file mode 100644 index 00000000..02ef15d0 --- /dev/null +++ b/SFtpComponent/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java new file mode 100644 index 00000000..3ab60bf6 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp; + +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.ftp.FtpTaskOption; +import com.arialyy.aria.util.CommonUtil; + +/** + * + * https://cloud.tencent.com/developer/article/1354612 + * @author lyy + */ +public class AbsSFtpInfoThread> + implements Runnable { + + private final String TAG = CommonUtil.getClassName(getClass()); + protected ENTITY mEntity; + protected TASK_WRAPPER mTaskWrapper; + protected FtpTaskOption mTaskOption; + private int mConnectTimeOut; + protected OnFileInfoCallback mCallback; + protected long mSize = 0; + protected String charSet = "UTF-8"; + private boolean isUpload = false; + + public AbsSFtpInfoThread(TASK_WRAPPER taskWrapper, OnFileInfoCallback callback) { + mTaskWrapper = taskWrapper; + mEntity = taskWrapper.getEntity(); + mTaskOption = (FtpTaskOption) taskWrapper.getTaskOption(); + mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut(); + mCallback = callback; + if (mEntity instanceof UploadEntity) { + isUpload = true; + } + } + + @Override public void run() { + + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java new file mode 100644 index 00000000..6c8b638c --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp; + +import android.text.TextUtils; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import java.util.Properties; + +/** + * sftp登录 + * + * @author lyy + */ +public class SFtpLogin { + + private String ip, userName, password; + private int port; + private Session session; + private boolean isLogin = false; + + private SFtpLogin() { + createClient(); + } + + /** + * 创建客户端 + */ + private void createClient() { + JSch jSch = new JSch(); + try { + if (TextUtils.isEmpty(userName)) { + session = jSch.getSession(userName, ip, port); + } else { + session = jSch.getSession(ip); + } + if (!TextUtils.isEmpty(password)) { + session.setPassword(password); + } + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config);// 为Session对象设置properties + session.setTimeout(3000);// 设置超时 + isLogin = true; + } catch (JSchException e) { + e.printStackTrace(); + } + } + + /** + * 执行登录 + */ + public Session login() { + try { + session.connect(); // 通过Session建立连接 + } catch (JSchException e) { + e.printStackTrace(); + } + return session; + } + + /** + * 登出 + */ + public void logout() { + if (session != null) { + session.disconnect(); + } + isLogin = false; + } + + public static class Builder { + private String ip, userName, password; + private int port = 22; + + public Builder setIp(String ip) { + this.ip = ip; + return this; + } + + public Builder setUserName(String userName) { + this.userName = userName; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setPort(int port) { + this.port = port; + return this; + } + + public SFtpLogin build() { + SFtpLogin login = new SFtpLogin(); + login.ip = ip; + login.userName = userName; + login.password = password; + login.port = port; + if (TextUtils.isEmpty(ip)) { + throw new IllegalArgumentException("ip不能为空"); + } + if (port < 0 || port > 65534) { + throw new IllegalArgumentException("端口错误"); + } + return login; + } + } +} diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index e4ea2d42..cb8d1c24 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -32,7 +32,7 @@ 3、只对新的多线程下载任务有效 4、只对多线程的任务有效 --> - + +## 预期行为 -## 什么问题 +## 当前行为 -## 如何复现此问题 +## 可能的解决方案 + -## 控制台日志 + +## 重现步骤 + + +1. +2. +3. +4. + + +## 错误日志 + + +## 版本 +* 框架版本 + +* 系统版本 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 6fffb0d7..c8eb2bbf 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -16,13 +16,10 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; -import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.ITargetHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; @@ -74,17 +71,13 @@ public class CheckDEntityUtil implements ICheckEntityUtil { mWrapper.getM3U8Params().setParams(IOptionConstant.cacheDir, cacheDir); M3U8Entity m3U8Entity = mEntity.getM3U8Entity(); - Object temp = mWrapper.getM3U8Params().getParam(IOptionConstant.generateIndexFileTemp); - boolean generateIndexFileTemp = temp != null && (boolean) temp; if (m3U8Entity == null) { m3U8Entity = new M3U8Entity(); m3U8Entity.setFilePath(mEntity.getFilePath()); m3U8Entity.setPeerIndex(0); m3U8Entity.setCacheDir(cacheDir); - m3U8Entity.setGenerateIndexFile(generateIndexFileTemp); m3U8Entity.insert(); } else { - m3U8Entity.setGenerateIndexFile(generateIndexFileTemp); m3U8Entity.update(); } if (mWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { @@ -140,15 +133,19 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private boolean checkPathConflicts(String filePath) { //设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径 if (!filePath.equals(mEntity.getFilePath())) { + + //if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { + // if (!mWrapper.isForceDownload()) { + // ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); + // return false; + // } else { + // ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); + // RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD); + // } + //} // 检查路径冲突 - if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { - if (!mWrapper.isForceDownload()) { - ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); - return false; - } else { - ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); - RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD); - } + if (!CheckUtil.checkAndHandlePathConflicts(mWrapper.isForceDownload(), filePath)) { + return false; } File newFile = new File(filePath); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java index 8dd23fc8..d9b56734 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -18,7 +18,6 @@ package com.arialyy.aria.core.download.m3u8; import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; -import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.ComponentUtil; @@ -27,8 +26,8 @@ import com.arialyy.aria.util.ComponentUtil; */ public class M3U8Option extends BaseOption { - private boolean generateIndexFileTemp = false; - private boolean mergeFile = false; + private boolean generateIndexFile = false; + private boolean mergeFile = true; private int bandWidth; private ITsMergeHandler mergeHandler; private IBandWidthUrlConverter bandWidthUrlConverter; @@ -41,9 +40,10 @@ public class M3U8Option extends BaseOption { /** * 生成m3u8索引文件 * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 + * 如果是直播文件下载,创建索引文件的操作将导致只能同时下载一个切片!! */ public OP generateIndexFile() { - this.generateIndexFileTemp = true; + this.generateIndexFile = true; return (OP) this; } diff --git a/DEV_LOG.md b/DEV_LOG.md index 45421b93..f487425f 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,9 @@ ## 开发日志 + + v_3.7.5 (2019/11/10) + - fix bug https://github.com/AriaLyy/Aria/issues/500 + - fix bug https://github.com/AriaLyy/Aria/issues/508 + - fix bug https://github.com/AriaLyy/Aria/issues/503 + - 修复m3u8创建索引不成功的问题 + v_3.7.4 (2019/11/2) - 修复一个class被莫名改变的问题 - 修复非分块模式下导致的一个下载失败问题 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java index a355a01b..2d7a7aa1 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java @@ -25,6 +25,7 @@ import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; import java.nio.charset.Charset; @@ -72,6 +73,9 @@ public class FtpDirInfoThread extends AbsFtpInfoThread mInfos = new ArrayList<>(); public interface OnGetLivePeerCallback { - void onGetPeer(String url); + void onGetPeer(String url, String extInf); } public M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { @@ -122,38 +118,51 @@ final public class M3U8InfoThread implements Runnable { } List extInf = new ArrayList<>(); boolean isLive = mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE; - boolean isGenerateIndexFile = mTaskWrapper.getEntity().getM3U8Entity().isGenerateIndexFile(); + boolean isGenerateIndexFile = + ((M3U8TaskOption) mTaskWrapper.getM3u8Option()).isGenerateIndexFile(); + // 写入索引信息的流 + FileOutputStream fos = null; if (isGenerateIndexFile) { - mInfos.add(line); + String indexPath = String.format(M3U8_INDEX_FORMAT, mEntity.getFilePath()); + File indexFile = new File(indexPath); + if (!indexFile.exists()) { + FileUtil.createFile(indexPath); + }else { + //FileUtil.deleteFile(indexPath); + } + fos = new FileOutputStream(indexFile); + ALog.d(TAG, line); + addIndexInfo(isGenerateIndexFile, fos, line); } while ((line = reader.readLine()) != null) { if (isStop) { break; } - if (isGenerateIndexFile) { - mInfos.add(line); - } + ALog.d(TAG, line); if (line.startsWith("#EXT-X-ENDLIST")) { + // 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志 + addIndexInfo(isGenerateIndexFile && !isLive, fos, line); break; - } - ALog.d(TAG, line); - if (line.startsWith("#EXTINF")) { - String info = reader.readLine(); - mInfos.add(info); + } else if (line.startsWith("#EXTINF")) { + String url = reader.readLine(); if (isLive) { if (onGetPeerCallback != null) { - onGetPeerCallback.onGetPeer(info); + onGetPeerCallback.onGetPeer(url, line); } } else { - extInf.add(info); + extInf.add(url); } + ALog.d(TAG, url); + addIndexInfo(isGenerateIndexFile && !isLive, fos, line); + addIndexInfo(isGenerateIndexFile && !isLive, fos, url); } else if (line.startsWith("#EXT-X-STREAM-INF")) { + addIndexInfo(isGenerateIndexFile, fos, line); int setBand = mM3U8Option.getBandWidth(); int bandWidth = getBandWidth(line); // 多码率的m3u8配置文件,清空信息 - if (isGenerateIndexFile && mInfos != null) { - mInfos.clear(); - } + //if (isGenerateIndexFile && mInfos != null) { + // mInfos.clear(); + //} if (setBand == 0) { handleBandWidth(conn, reader.readLine()); } else if (bandWidth == setBand) { @@ -162,8 +171,11 @@ final public class M3U8InfoThread implements Runnable { failDownload(String.format("【%s】码率不存在", bandWidth), false); } return; - } else if (line.startsWith("EXT-X-KEY")) { + } else if (line.startsWith("#EXT-X-KEY")) { + addIndexInfo(isGenerateIndexFile, fos, line); getKeyInfo(line); + } else { + addIndexInfo(isGenerateIndexFile, fos, line); } } @@ -177,8 +189,11 @@ final public class M3U8InfoThread implements Runnable { } CompleteInfo info = new CompleteInfo(); info.obj = extInf; - generateIndexFile(); + onFileInfoCallback.onComplete(mEntity.getKey(), info); + if (fos != null) { + fos.close(); + } } else if (code == HttpURLConnection.HTTP_MOVED_TEMP || code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_SEE_OTHER @@ -193,40 +208,19 @@ final public class M3U8InfoThread implements Runnable { } /** - * 创建索引文件 + * 添加切片信息到索引文件中 + * 直播下载的索引只记录头部信息,不记录EXTINF中的信息,该信息在onGetPeer的方法中添加。 + * 点播下载记录所有信息 + * + * @param write true 将信息写入文件 + * @param info 切片信息 */ - private void generateIndexFile() { - if (mTaskWrapper.getEntity().getM3U8Entity().isGenerateIndexFile()) { - - String indexPath = String.format(M3U8_INDEX_FORMAT, mEntity.getFilePath()); - File indexFile = new File(indexPath); - if (indexFile.exists()) { - FileUtil.deleteFile(indexPath); - } - FileUtil.createFile(indexPath); - - FileOutputStream fos = null; - try { - fos = new FileOutputStream(indexFile); - for (String str : mInfos) { - byte[] by = str.concat("\r\n").getBytes(Charset.forName("UTF-8")); - fos.write(by, 0, by.length); - } - fos.flush(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } + private void addIndexInfo(boolean write, FileOutputStream fos, String info) + throws IOException { + if (!write) { + return; } + fos.write(info.concat("\r\n").getBytes(Charset.forName("UTF-8"))); } /** diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 25de650f..8ba21d6b 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -24,6 +24,7 @@ import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.RecordUtil; import java.io.File; import java.util.ArrayList; @@ -42,7 +43,7 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { @Override public void onPre() { super.onPre(); - if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE){ + if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); } } @@ -54,18 +55,22 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { String cacheDir = mOption.getCacheDir(); long currentProgress = 0; int completeNum = 0; + File targetFile = new File(mTaskRecord.filePath); + if (!targetFile.exists()){ + FileUtil.createFile(targetFile); + } M3U8Entity m3U8Entity = ((DownloadEntity) getEntity()).getM3U8Entity(); // 重新下载所有切片 boolean reDownload = - (m3U8Entity.getPeerNum() <= 0 || (m3U8Entity.isGenerateIndexFile() && !new File( + (m3U8Entity.getPeerNum() <= 0 || (mOption.isGenerateIndexFile() && !new File( String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath())).exists())); for (ThreadRecord record : mTaskRecord.threadRecords) { File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); if (!record.isComplete || reDownload) { if (temp.exists()) { - temp.delete(); + FileUtil.deleteFile(temp); } record.startLocation = 0; //ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId)); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index 591cbf87..cd1c663d 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -101,14 +101,14 @@ public class M3U8TaskOption implements ITaskOption { /** * 生成索引占位字段 */ - private boolean generateIndexFileTemp = false; + private boolean generateIndexFile = false; - public boolean isGenerateIndexFileTemp() { - return generateIndexFileTemp; + public boolean isGenerateIndexFile() { + return generateIndexFile; } - public void setGenerateIndexFileTemp(boolean generateIndexFileTemp) { - this.generateIndexFileTemp = generateIndexFileTemp; + public void setGenerateIndexFile(boolean generateIndexFile) { + this.generateIndexFile = generateIndexFile; } public int getJumpIndex() { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java index dee64820..41d86041 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java @@ -89,6 +89,14 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { } } + int code = conn.getResponseCode(); + if (code != HttpURLConnection.HTTP_OK) { + fail(new TaskException(TAG, + String.format("连接错误,http错误码:%s,url:%s", code, getThreadConfig().url)), + false); + return; + } + is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); if (mHttpTaskOption.isChunked()) { readChunked(is); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 297e5322..f474d7dd 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -24,17 +24,22 @@ import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; +import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.m3u8.BaseM3U8Loader; -import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.m3u8.IdGenerator; import com.arialyy.aria.m3u8.M3U8Listener; +import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; import java.io.File; +import java.io.FileOutputStream; import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; @@ -42,6 +47,8 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +import static com.arialyy.aria.m3u8.M3U8InfoThread.M3U8_INDEX_FORMAT; + /** * M3U8点播文件下载器 */ @@ -49,15 +56,21 @@ public class M3U8LiveLoader extends BaseM3U8Loader { /** * 最大执行数 */ - private static final int EXEC_MAX_NUM = 4; + private static int EXEC_MAX_NUM = 4; private Handler mStateHandler; private ArrayBlockingQueue mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); private ReentrantLock LOCK = new ReentrantLock(); private Condition mCondition = LOCK.newCondition(); - private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); + private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); + private ExtInfo mCurExtInfo; + private FileOutputStream mIndexFos; M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) { super(listener, wrapper); + if (((M3U8TaskOption) wrapper.getM3u8Option()).isGenerateIndexFile()) { + ALog.i(TAG, "直播文件下载,创建索引文件的操作将导致只能同时下载一个切片"); + EXEC_MAX_NUM = 1; + } } @Override protected IThreadState createStateManager(Looper looper) { @@ -66,8 +79,8 @@ public class M3U8LiveLoader extends BaseM3U8Loader { return manager; } - void offerPeer(String peerUrl) { - mPeerQueue.offer(peerUrl); + void offerPeer(ExtInfo extInfo) { + mPeerQueue.offer(extInfo); } @Override protected void handleTask() { @@ -80,13 +93,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader { try { LOCK.lock(); while (mFlagQueue.size() < EXEC_MAX_NUM) { - String url = mPeerQueue.poll(); - if (url == null) { + ExtInfo extInfo = mPeerQueue.poll(); + if (extInfo == null) { break; } - ThreadTask task = createThreadTask(cacheDir, index, url); + mCurExtInfo = extInfo; + ThreadTask task = createThreadTask(cacheDir, index, extInfo.url); getTaskList().put(index, task); - mFlagQueue.offer(startThreadTask(task)); + mFlagQueue.offer(startThreadTask(task, task.getConfig().peerIndex)); index++; } if (mFlagQueue.size() > 0) { @@ -106,11 +120,15 @@ public class M3U8LiveLoader extends BaseM3U8Loader { return mTempFile.length(); } - private void notifyLock() { + private void notifyLock(boolean success, int peerId) { try { LOCK.lock(); long id = mFlagQueue.take(); - ALog.d(TAG, String.format("线程【%s】完成", id)); + if (success) { + ALog.d(TAG, String.format("切片【%s】下载成功", peerId)); + } else { + ALog.e(TAG, String.format("切片【%s】下载失败", peerId)); + } mCondition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); @@ -119,13 +137,27 @@ public class M3U8LiveLoader extends BaseM3U8Loader { } } + @Override protected void onPostStop() { + super.onPostStop(); + if (mIndexFos != null) { + try { + mIndexFos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + /** * 启动线程任务 * * @return 线程唯一id标志 */ - private long startThreadTask(ThreadTask task) { + private long startThreadTask(ThreadTask task, int indexId) { ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task); + ((M3U8Listener) mListener).onPeerStart(mTaskWrapper.getKey(), + task.getConfig().tempFile.getPath(), + indexId); return IdGenerator.getInstance().nextId(); } @@ -139,6 +171,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { record.tsUrl = tsUrl; record.threadType = TaskRecord.TYPE_M3U8_LIVE; record.threadId = indexId; + mRecord.threadRecords.add(record); SubThreadConfig config = new SubThreadConfig(); config.url = tsUrl; @@ -148,6 +181,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { config.taskWrapper = mTaskWrapper; config.record = record; config.stateHandler = mStateHandler; + config.peerIndex = indexId; if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile); @@ -163,10 +197,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { * * @return {@code true} 合并成功,{@code false}合并失败 */ - public boolean mergeFile() { - if (getEntity().getM3U8Entity().isGenerateIndexFile()) { - return generateIndexFile(); - } + boolean mergeFile() { ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); String cacheDir = getCacheDir(); List partPath = new ArrayList<>(); @@ -213,7 +244,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { /** * 任务状态回调 */ - private IEventListener mListener; + private M3U8Listener mListener; private long mProgress; //当前总进度 private Looper mLooper; @@ -222,7 +253,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { */ LiveStateManager(Looper looper, IEventListener listener) { mLooper = looper; - mListener = listener; + mListener = (M3U8Listener) listener; } /** @@ -234,6 +265,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { } @Override public boolean handleMessage(Message msg) { + int peerIndex = msg.getData().getInt(ISchedulers.DATA_M3U8_PEER_INDEX); switch (msg.what) { case STATE_STOP: if (isBreak()) { @@ -248,15 +280,46 @@ public class M3U8LiveLoader extends BaseM3U8Loader { } break; case STATE_COMPLETE: - notifyLock(); + notifyLock(true, peerIndex); + if (mM3U8Option.isGenerateIndexFile() && !isBreak()) { + addExtInf(mCurExtInfo.url, mCurExtInfo.extInf); + } + mListener.onPeerComplete(mTaskWrapper.getKey(), + msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex); break; case STATE_RUNNING: mProgress += (long) msg.obj; break; + case STATE_FAIL: + notifyLock(false, peerIndex); + mListener.onPeerFail(mTaskWrapper.getKey(), + msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex); + break; } return false; } + /** + * 给索引文件添加extInfo信息 + */ + private void addExtInf(String url, String extInf) { + File indexFile = + new File(String.format(M3U8_INDEX_FORMAT, getEntity().getFilePath())); + if (!indexFile.exists()) { + ALog.e(TAG, String.format("索引文件【%s】不存在,添加peer的extInf失败", indexFile.getPath())); + return; + } + try { + if (mIndexFos == null) { + mIndexFos = new FileOutputStream(indexFile, true); + } + mIndexFos.write(extInf.concat("\r\n").getBytes(Charset.forName("UTF-8"))); + mIndexFos.write(url.concat("\r\n").getBytes(Charset.forName("UTF-8"))); + } catch (IOException e) { + e.printStackTrace(); + } + } + @Override public boolean isFail() { return false; } @@ -269,4 +332,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader { return mProgress; } } + + static class ExtInfo { + String url; + String extInf; + + ExtInfo(String url, String extInf) { + this.url = url; + this.extInf = extInf; + } + } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index 3620fc03..17a62bcf 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -27,6 +27,7 @@ import com.arialyy.aria.core.processor.ILiveTsUrlConverter; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; +import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoThread; import com.arialyy.aria.m3u8.M3U8Listener; @@ -87,7 +88,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { } }); infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() { - @Override public void onGetPeer(String url) { + @Override public void onGetPeer(String url, String extInf) { if (mPeerUrls.contains(url)) { return; } @@ -104,7 +105,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { fail(new M3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false); return; } - getLoader().offerPeer(url); + getLoader().offerPeer(new M3U8LiveLoader.ExtInfo(url, extInf)); } }); return infoThread; @@ -129,7 +130,13 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { if (mInfoThread != null) { mInfoThread.setStop(true); closeTimer(); - if (mM3U8Option.isMergeFile()) { + if (((M3U8TaskOption) getTaskWrapper().getM3u8Option()).isGenerateIndexFile()) { + if (getLoader().generateIndexFile(true)) { + getListener().onComplete(); + } else { + getListener().onFail(false, new TaskException(TAG, "创建索引文件失败")); + } + } else if (mM3U8Option.isMergeFile()) { if (getLoader().mergeFile()) { getListener().onComplete(); } else { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 19211a57..c88fa5e2 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -31,14 +31,16 @@ import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; +import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.m3u8.BaseM3U8Loader; -import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.FileUtil; import java.io.File; import java.util.ArrayList; @@ -219,6 +221,12 @@ public class M3U8VodLoader extends BaseM3U8Loader { } } mManager.updateStateCount(); + if (mCompleteNum <= 0){ + mListener.onStart(0); + }else { + int percent = mCompleteNum * 100 / mRecord.threadRecords.size(); + mListener.onResume(percent); + } } /** @@ -446,7 +454,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { * M3U8线程状态管理 */ private class VodStateManager implements IThreadState { - private final String TAG = "M3U8ThreadStateManager"; + private final String TAG = CommonUtil.getClassName(VodStateManager.class); /** * 任务状态回调 @@ -456,7 +464,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { private int cancelNum = 0; // 已经取消的线程的数 private int stopNum = 0; // 已经停止的线程数 private int failNum = 0; // 失败的线程数 - private long progress; //当前总进度 + private long progress; //当前总进度,百分比进度 private TaskRecord taskRecord; // 任务记录 private Looper looper; @@ -559,7 +567,14 @@ public class M3U8VodLoader extends BaseM3U8Loader { "startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s", startThreadNum, stopNum, cancelNum, failNum, mCompleteNum, mFlagQueue.size())); ALog.d(TAG, String.format("vod任务【%s】完成", mTempFile.getName())); - if (mM3U8Option.isMergeFile()) { + + if (mM3U8Option.isGenerateIndexFile()) { + if (generateIndexFile(false)){ + listener.onComplete(); + }else { + listener.onFail(false, new TaskException(TAG, "创建索引文件失败")); + } + } else if (mM3U8Option.isMergeFile()) { if (mergeFile()) { listener.onComplete(); } else { @@ -572,7 +587,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } break; case STATE_RUNNING: - progress += (long) msg.obj; + //progress += (long) msg.obj; break; } return true; @@ -596,6 +611,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { int percent = completeNum * 100 / taskRecord.threadRecords.size(); getEntity().setPercent(percent); getEntity().update(); + progress = percent; } @Override public boolean isFail() { @@ -625,9 +641,6 @@ public class M3U8VodLoader extends BaseM3U8Loader { * @return {@code true} 合并成功,{@code false}合并失败 */ private boolean mergeFile() { - if (getEntity().getM3U8Entity().isGenerateIndexFile()) { - return generateIndexFile(); - } ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); String cacheDir = getCacheDir(); List partPath = new ArrayList<>(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java index 05a01a41..2a04816f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java @@ -58,11 +58,6 @@ public class M3U8Entity extends DbEntity implements Parcelable { */ private String cacheDir; - /** - * 生成索引 - */ - private boolean generateIndexFile; - /** * 加密key保存地址 */ @@ -115,14 +110,6 @@ public class M3U8Entity extends DbEntity implements Parcelable { this.iv = iv; } - public boolean isGenerateIndexFile() { - return generateIndexFile; - } - - public void setGenerateIndexFile(boolean generateIndexFile) { - this.generateIndexFile = generateIndexFile; - } - public boolean isLive() { return isLive; } @@ -248,7 +235,6 @@ public class M3U8Entity extends DbEntity implements Parcelable { dest.writeInt(this.peerNum); dest.writeByte(this.isLive ? (byte) 1 : (byte) 0); dest.writeString(this.cacheDir); - dest.writeByte(this.generateIndexFile ? (byte) 1 : (byte) 0); dest.writeString(this.keyPath); dest.writeString(this.keyUrl); dest.writeString(this.method); @@ -261,7 +247,6 @@ public class M3U8Entity extends DbEntity implements Parcelable { this.peerNum = in.readInt(); this.isLive = in.readByte() != 0; this.cacheDir = in.readString(); - this.generateIndexFile = in.readByte() != 0; this.keyPath = in.readString(); this.keyUrl = in.readString(); this.method = in.readString(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index fdd80a2f..0b1999a8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -80,7 +80,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { /** * 初始化组合任务状态 */ - protected void initState() { + private void initState() { mState = new GroupRunState(getWrapper().getKey(), mListener, mGTWrapper.getSubTaskWrapper().size(), mSubQueue); for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index 1fd67739..e778b5f6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -200,8 +200,7 @@ public class NormalLoader extends AbsLoader { ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); getEntity().setCurrentProgress(currentProgress); } - mStateHandler.obtainMessage(IThreadState.STATE_UPDATE_PROGRESS, currentProgress) - .sendToTarget(); + mStateManager.updateProgress(currentProgress); startThreadTask(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java index b7fec6fa..1e6fc16a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java @@ -19,9 +19,9 @@ import android.os.Bundle; import android.os.Looper; import android.os.Message; import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; @@ -59,6 +59,13 @@ public class ThreadStateManager implements IThreadState { mListener = listener; } + /** + * 不要使用handle更新启动线程的进度,因为有延迟 + */ + void updateProgress(long curProgress) { + mProgress = curProgress; + } + @Override public boolean handleMessage(Message msg) { switch (msg.what) { case STATE_STOP: diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java index 1515e27a..2865781b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java @@ -22,7 +22,7 @@ import com.arialyy.aria.util.CommonUtil; import java.io.File; /** - * 但文件任务适配器 + * 单文件任务适配器 * * @Author lyy * @Date 2019-09-19 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java index f9d9679a..4869426c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java @@ -97,7 +97,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { return mThreadConfig; } - @Override public void setThreadStateObserver(IThreadTaskObserver observer) { + @Override public void attach(IThreadTaskObserver observer) { mObserver = observer; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java index f03deb65..1e1a1102 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java @@ -36,7 +36,7 @@ public interface IThreadTaskAdapter { void setMaxSpeed(int speed); /** - * 设置线程任务状态观察者 + * 注册观察者 */ - void setThreadStateObserver(IThreadTaskObserver observer); + void attach(IThreadTaskObserver observer); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 23722c8b..23df702b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -33,6 +33,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.NetUtils; @@ -49,7 +50,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * 线程重试次数 */ private final int RETRY_NUM = 2; - private final String TAG = "AbsThreadTask"; + private final String TAG = CommonUtil.getClassName(getClass()); private IEntity mEntity; protected AbsTaskWrapper mTaskWrapper; private int mFailTimes = 0; @@ -94,7 +95,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { */ public void setAdapter(IThreadTaskAdapter adapter) { mAdapter = adapter; - mAdapter.setThreadStateObserver(this); + mAdapter.attach(this); } /** @@ -335,7 +336,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { */ protected void fail(final long subCurrentLocation, BaseException ex, boolean needRetry) { if (ex != null) { - ALog.e(TAG, ALog.getExceptionString(ex)); + ex.printStackTrace(); } if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { writeConfig(false, 0); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 34149011..28915e3e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -17,6 +17,9 @@ package com.arialyy.aria.util; import android.text.TextUtils; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.orm.DbEntity; import java.lang.reflect.Modifier; import java.util.List; @@ -27,6 +30,27 @@ import java.util.List; public class CheckUtil { private static final String TAG = "CheckUtil"; + /** + * 检查和处理路径冲突 + * + * @param isForceDownload true,如果路径冲突,将删除其它任务的记录的 + * @param filePath 文件保存路径 + * @return false 任务不再执行,true 任务继续执行 + */ + public static boolean checkAndHandlePathConflicts(boolean isForceDownload, String filePath) { + if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { + if (!isForceDownload) { + ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); + return false; + } else { + ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); + RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD); + return true; + } + } + return true; + } + /** * 检查成员类是否是静态和public */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index a76cf20d..5ce2fba6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -295,7 +295,7 @@ public class RecordUtil { } /** - * 删除ts文件 + * 删除ts文件,和索引文件(如果有的话) */ private static void removeTsCache(File targetFile, long bandWidth) { @@ -321,6 +321,12 @@ public class RecordUtil { cDir.delete(); } } + + File indexFile = new File(String.format("%s.index", targetFile.getPath())); + + if (indexFile.exists()) { + indexFile.delete(); + } } /** diff --git a/README.md b/README.md index f7fe2dae..a1adfe46 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Aria有以下特点: [怎样使用Aria?](#使用) -如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^` +如果你觉得Aria对你有帮助,你的star和issues将是对我最大支持,当然,也非常欢迎你能PR,[PR方法](https://www.zhihu.com/question/21682976/answer/79489643)`^_^` ## 示例 * 多任务下载 @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.4-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.4-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.4-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.4-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.5-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.5-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.5-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.5-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.4' -annotationProcessor 'com.arialyy.aria:compiler:3.7.4' -implementation 'com.arialyy.aria:ftpComponent:3.7.4' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.4' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.5' +annotationProcessor 'com.arialyy.aria:compiler:3.7.5' +implementation 'com.arialyy.aria:ftpComponent:3.7.5' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.5' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,10 +137,11 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.4 (2019/11/2) - - 修复一个class被莫名改变的问题 - - 修复非分块模式下导致的一个下载失败问题 - - fix bug https://github.com/AriaLyy/Aria/issues/493 ++ v_3.7.5 (2019/11/10) + - fix bug https://github.com/AriaLyy/Aria/issues/500 + - fix bug https://github.com/AriaLyy/Aria/issues/508 + - fix bug https://github.com/AriaLyy/Aria/issues/503 + - 修复m3u8创建索引不成功的问题 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) @@ -164,7 +165,7 @@ protected void onCreate(Bundle savedInstanceState) { 在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。
## 打赏 - 如果觉得框架写的不错并且帮助到了你,可以请我喝杯热饮
+ 如果觉得框架写的不错并且帮助到了你,可以请我喝杯热茶。`^_^`
diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java new file mode 100644 index 00000000..858cf640 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp; + +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; + +/** + * 获取sftp文件信息 + * + * @author lyy + */ +public class AbsSFtpFileInfoThread implements Runnable { + + private WP mWrapper; + private SFtpUtil mUtil; + private OnFileInfoCallback mCallback; + + public AbsSFtpFileInfoThread(SFtpUtil util, WP wrapper, OnFileInfoCallback callback) { + mWrapper = wrapper; + mUtil = util; + mCallback = callback; + } + + @Override public void run() { + startFlow(); + } + + private void startFlow() { + + } + + private ChannelSftp createChannel() { + ChannelSftp sftp = null; + try { + sftp = (ChannelSftp) mUtil.getSession().openChannel(SFtpUtil.CMD_TYPE_SFTP); + sftp.connect(); + } catch (JSchException e) { + e.printStackTrace(); + } + return sftp; + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java similarity index 59% rename from SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java rename to SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java index 6c8b638c..1fa1c1d8 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java @@ -16,24 +16,39 @@ package com.arialyy.aria.sftp; import android.text.TextUtils; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.Properties; /** - * sftp登录 + * sftp工具类 * * @author lyy */ -public class SFtpLogin { - +public class SFtpUtil { + private final String TAG = CommonUtil.getClassName(getClass()); + /** + * 用于执行命令 + */ + public static final String CMD_TYPE_EXEC = "exec"; + /** + * 用于处理文件 + */ + public static final String CMD_TYPE_SFTP = "sftp"; private String ip, userName, password; private int port; private Session session; private boolean isLogin = false; - private SFtpLogin() { + private SFtpUtil() { createClient(); } @@ -55,6 +70,7 @@ public class SFtpLogin { config.put("StrictHostKeyChecking", "no"); session.setConfig(config);// 为Session对象设置properties session.setTimeout(3000);// 设置超时 + login(); isLogin = true; } catch (JSchException e) { e.printStackTrace(); @@ -83,6 +99,65 @@ public class SFtpLogin { isLogin = false; } + public Session getSession() { + return session; + } + + /** + * 执行命令 + * + * @param cmd sftp命令 + */ + public void execCommand(String cmd) { + if (TextUtils.isEmpty(cmd)) { + ALog.e(TAG, "命令为空"); + return; + } + if (!isLogin) { + ALog.e(TAG, "没有登录"); + return; + } + ChannelExec channel = null; + try { + channel = (ChannelExec) session.openChannel(CMD_TYPE_EXEC); + channel.setCommand(cmd); + channel.connect(); + String rst = getResult(channel.getInputStream()); + + ALog.i(TAG, String.format("result: %s", rst)); + } catch (IOException e) { + e.printStackTrace(); + } catch (JSchException e) { + e.printStackTrace(); + } finally { + if (channel != null) { + channel.disconnect(); + } + } + } + + /** + * 执行命令后,获取服务器端返回的数据 + * + * @return 服务器端返回的数据 + */ + private String getResult(InputStream in) throws IOException { + if (in == null){ + ALog.e(TAG, "输入流为空"); + return null; + } + StringBuilder sb = new StringBuilder(); + BufferedReader isr = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = isr.readLine()) != null) { + sb.append(line); + } + in.close(); + isr.close(); + + return sb.toString(); + } + public static class Builder { private String ip, userName, password; private int port = 22; @@ -107,8 +182,8 @@ public class SFtpLogin { return this; } - public SFtpLogin build() { - SFtpLogin login = new SFtpLogin(); + public SFtpUtil build() { + SFtpUtil login = new SFtpUtil(); login.ip = ip; login.userName = userName; login.password = password; diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java new file mode 100644 index 00000000..ba0e61e8 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp.download; + +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.ftp.download.FtpDLoaderAdapter; + +/** + * sftp下载适配器 + * + * @author lyy + */ +final class SFtpDLoaderAdapter extends FtpDLoaderAdapter { + SFtpDLoaderAdapter(ITaskWrapper wrapper) { + super(wrapper); + } + + @Override public IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask threadTask = new ThreadTask(config); + SFtpDThreadTaskAdapter adapter = new SFtpDThreadTaskAdapter(config); + threadTask.setAdapter(adapter); + return threadTask; + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java new file mode 100644 index 00000000..5b691cf9 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp.download; + +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.loader.AbsLoader; +import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.ftp.FtpTaskOption; +import com.arialyy.aria.sftp.AbsSFtpFileInfoThread; +import com.arialyy.aria.sftp.SFtpUtil; + +/** + * sftp下载工具 + * + * @author lyy + */ +public class SFtpDLoaderUtil extends AbsNormalLoaderUtil { + + private SFtpUtil mSftpUtil; + + protected SFtpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { + super(wrapper, listener); + wrapper.generateTaskOption(FtpTaskOption.class); + FtpTaskOption option = (FtpTaskOption) wrapper.getTaskOption(); + mSftpUtil = new SFtpUtil.Builder() + .setIp(option.getUrlEntity().hostName) + .setPort(Integer.parseInt(option.getUrlEntity().port)) + .setUserName(option.getUrlEntity().user) + .setPassword(option.getUrlEntity().password) + .build(); + } + + @Override protected AbsLoader createLoader() { + NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); + loader.setAdapter(new SFtpDLoaderAdapter(getTaskWrapper())); + return loader; + } + + @Override protected Runnable createInfoThread() { + return new AbsSFtpFileInfoThread<>(mSftpUtil, (DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { + @Override public void onComplete(String key, CompleteInfo info) { + + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + + mSftpUtil.logout(); + } + }); + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java new file mode 100644 index 00000000..e4f4caf2 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp.download; + +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.task.AbsThreadTaskAdapter; + +/** + * sftp 线程任务适配器 + * + * @author lyy + */ +public class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter { + + SFtpDThreadTaskAdapter(SubThreadConfig config) { + super(config); + } + + @Override protected void handlerThreadTask() { + + } +} diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java index 046eb007..800ec4e3 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java @@ -71,6 +71,29 @@ public class DownloadModule extends BaseModule { return list; } + /** + * 创建m3u8下载地址 + */ + public List createM3u8TestList(){ + String[] names = new String[]{"m3u8test1.ts", "m3u8test2.ts"}; + String[] urls = new String[]{ + "http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8", + "http://qn.shytong.cn/8f4011f2a31bd347da42b54fe37a7ba8-transcode.m3u8" + }; + List list = new ArrayList<>(); + int i = 0; + for (String name : names) { + FileListEntity entity = new FileListEntity(); + entity.name = name; + entity.key = urls[i]; + entity.type = 2; + entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + name; + list.add(entity); + i++; + } + return list; + } + private String[] getStringArray(int array) { return getContext().getResources().getStringArray(array); } @@ -91,7 +114,7 @@ public class DownloadModule extends BaseModule { FileListEntity entity = new FileListEntity(); entity.urls = getStringArray(urls); entity.names = getStringArray(names); - entity.isGroup = true; + entity.type = 1; entity.name = alias; entity.key = CommonUtil.getMd5Code(Arrays.asList(entity.urls)); entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + alias; diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 64c0b12c..48a9f3cb 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -215,6 +215,7 @@ public class SingleTaskActivity extends BaseActivity { @Download.onTaskResume void taskResume(DownloadTask task) { + ALog.d(TAG, "resume"); if (task.getKey().equals(mUrl)) { getBinding().setStateStr(getString(R.string.stop)); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index 8f8c0c1c..a5fb594f 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -24,12 +24,14 @@ import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.util.ALog; import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityDownloadGroupBinding; import com.arialyy.simple.widget.SubStateLinearLayout; +import java.security.AlgorithmConstraints; /** * Created by lyy on 2017/7/6. @@ -128,6 +130,7 @@ public class FTPDirDownloadActivity extends BaseActivity public void onClick(View view) { switch (view.getId()) { case R.id.start: - startD(); - if (!AppUtil.chekEntityValid(mEntity)) { - startD(); - break; - } if (Aria.download(this).load(mEntity.getId()).isRunning()) { Aria.download(this).load(mEntity.getId()).stop(); } else { - Aria.download(this).load(mEntity.getId()) - .m3u8LiveOption(getLiveoption()) - .resume(); + startD(); } break; case R.id.cancel: @@ -245,6 +238,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity private M3U8LiveOption getLiveoption() { M3U8LiveOption option = new M3U8LiveOption(); option + //.generateIndexFile() .setLiveTsUrlConvert(new LiveTsUrlConverter()); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index f04b6041..3f27fcfd 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -90,10 +90,10 @@ public class M3U8VodDLoadActivity extends BaseActivity { getBinding().setFilePath(entity.getFilePath()); mUrl = entity.getUrl(); mFilePath = entity.getFilePath(); - mVideoFragment = new VideoPlayerFragment(0, entity); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(R.id.video_content, mVideoFragment); - ft.commit(); + //mVideoFragment = new VideoPlayerFragment(0, entity); + //FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + //ft.add(R.id.video_content, mVideoFragment); + //ft.commit(); } }); getBinding().setViewModel(this); @@ -176,7 +176,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @M3U8.onPeerComplete void onPeerComplete(String m3u8Url, String peerPath, int peerIndex) { //ALog.d(TAG, "peer complete, path: " + peerPath + ", index: " + peerIndex); - mVideoFragment.addPlayer(peerIndex, peerPath); + //mVideoFragment.addPlayer(peerIndex, peerPath); } @M3U8.onPeerFail @@ -217,6 +217,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @Download.onTaskResume void taskResume(DownloadTask task) { + ALog.d(TAG, "m3u8 vod resume"); if (task.getKey().equals(mUrl)) { getBinding().setStateStr(getString(R.string.stop)); } @@ -303,9 +304,10 @@ public class M3U8VodDLoadActivity extends BaseActivity { private M3U8VodOption getM3U8Option() { M3U8VodOption option = new M3U8VodOption(); option - .generateIndexFile() - .setVodTsUrlConvert(new VodTsUrlConverter()) - .setMergeHandler(new TsMergeHandler()); + //.generateIndexFile() + //.merge(true) + .setVodTsUrlConvert(new VodTsUrlConverter()); + //.setMergeHandler(new TsMergeHandler()); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java index d1fec3c2..0a9c37cf 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java @@ -35,10 +35,12 @@ public class M3U8VodModule extends BaseViewModule { //private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8"; // 多码率地址: //private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; - private final String defUrl = "http://cn1.fa1244.cn/hls/20190516/6d271eaa73b2e4cb51d13831b0c1ab4c/1557976262/index.m3u8"; + //private final String defUrl = "http://cn1.fa1244.cn/hls/20190516/6d271eaa73b2e4cb51d13831b0c1ab4c/1557976262/index.m3u8"; + private final String defUrl = "http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8"; private final String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() - + "/道士下山.ts"; + //+ "/道士下山.ts"; + + "/bb1.ts"; private MutableLiveData liveData = new MutableLiveData<>(); private DownloadEntity singDownloadInfo; diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java index ef05d29a..54b05d65 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java @@ -28,7 +28,7 @@ import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; import com.arialyy.simple.R; import com.arialyy.simple.base.adapter.AbsHolder; @@ -152,12 +152,19 @@ public class DownloadAdapter extends AbsRVAdapter convert(String m3u8Url, List tsUrls) { + List temp = new ArrayList<>(); + for (String tsUrl : tsUrls){ + temp.add("http://qn.shytong.cn" + tsUrl); + } + + return temp; + } + } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java index b6d5ca52..b22febd8 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java @@ -22,7 +22,13 @@ package com.arialyy.simple.core.download.mutil; public class FileListEntity { public String name, key, downloadPath; - public boolean isGroup = false; + + /** + * 0:普通任务 + * 1:组合任务 + * 2:m3u8任务 + */ + public int type = 0; public String[] urls, names; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java index b740822a..b54bce13 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java @@ -113,7 +113,7 @@ public class MultiDownloadActivity extends BaseActivity { setTitle("多任务下载"); mData.addAll(getModule(DownloadModule.class).createGroupTestList()); mData.addAll(getModule(DownloadModule.class).createMultiTestList()); + mData.addAll(getModule(DownloadModule.class).createM3u8TestList()); mAdapter = new FileListAdapter(this, mData); mList = getBinding().list; mBar = findViewById(R.id.toolbar); diff --git a/build.gradle b/build.gradle index 5bd0ce9c..4ba0e57e 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 374 - versionName = '3.7.4' + versionCode = 375 + versionName = '3.7.5' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 846e6c0720d9aa73175575efcbdb511c053bd809 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 18 Nov 2019 19:35:06 +0800 Subject: [PATCH 020/140] =?UTF-8?q?fix=20bug=20https://github.com/AriaLyy/?= =?UTF-8?q?Aria/issues/516=20fix=20bug=20https://github.com/AriaLyy/Aria/i?= =?UTF-8?q?ssues/505=20=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=BC=A0=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=BC=BA=E5=88=B6=E4=B8=8A=E4=BC=A0=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/command/CommandManager.java | 6 +- .../aria/core/command/ResumeAllCmd.java | 115 +++------- .../aria/core/command/ResumeThread.java | 198 ++++++++++++++++++ .../arialyy/aria/core/command/StartCmd.java | 89 +------- .../common/controller/BuilderController.java | 1 + .../aria/core/download/CheckDEntityUtil.java | 12 +- .../core/manager/DGTaskWrapperFactory.java | 15 +- .../core/manager/DTaskWrapperFactory.java | 9 +- .../core/manager/UTaskWrapperFactory.java | 10 +- .../aria/core/upload/CheckUEntityUtil.java | 7 +- .../core/upload/target/FtpBuilderTarget.java | 13 +- .../core/upload/target/FtpNormalTarget.java | 4 +- .../core/upload/target/HttpBuilderTarget.java | 9 + DEV_LOG.md | 5 + .../arialyy/aria/ftp/FtpDirInfoThread.java | 2 +- .../arialyy/aria/ftp/FtpRecordAdapter.java | 6 +- .../ftp/download/FtpDThreadTaskAdapter.java | 2 +- .../arialyy/aria/http/HttpRecordAdapter.java | 6 +- .../http/download/HttpDThreadTaskAdapter.java | 2 +- .../arialyy/aria/m3u8/M3U8RecordAdapter.java | 8 +- .../aria/m3u8/M3U8ThreadTaskAdapter.java | 2 +- .../aria/m3u8/live/M3U8LiveLoader.java | 1 - .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 1 - .../com/arialyy/aria/core/TaskRecord.java | 6 - .../aria/core/common/RecordHandler.java | 1 - .../aria/core/common/RecordHelper.java | 8 +- .../aria/core/common/SubThreadConfig.java | 2 - .../core/download/DownloadGroupEntity.java | 8 +- .../aria/core/loader/NormalLoader.java | 1 - .../arialyy/aria/core/task/ThreadTask.java | 2 - .../aria/core/upload/UTaskWrapper.java | 13 ++ .../aria/core/upload/UploadEntity.java | 6 +- .../aria/core/wrapper/AbsTaskWrapper.java | 1 + .../java/com/arialyy/aria/orm/DBConfig.java | 2 +- .../java/com/arialyy/aria/orm/SqlHelper.java | 4 +- .../java/com/arialyy/aria/util/CheckUtil.java | 24 ++- .../com/arialyy/aria/util/ComponentUtil.java | 3 + .../java/com/arialyy/aria/util/ErrorHelp.java | 12 +- .../com/arialyy/aria/util/RecordUtil.java | 2 +- .../core/download/HttpDownloadModule.java | 2 + .../simple/core/upload/FtpUploadActivity.java | 65 ++++-- 41 files changed, 401 insertions(+), 284 deletions(-) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/CommandManager.java b/Aria/src/main/java/com/arialyy/aria/core/command/CommandManager.java index 6ca3a813..79c9427f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/CommandManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/CommandManager.java @@ -30,7 +30,7 @@ public class CommandManager { EventMsgUtil.getDefault().register(this); } - public static CommandManager init() { + public static void init() { if (instance == null) { synchronized (CommandManager.class) { if (instance == null) { @@ -38,7 +38,6 @@ public class CommandManager { } } } - return instance; } @Event @@ -51,9 +50,6 @@ public class CommandManager { @Event public void start(StartCmd cmd) { - if (CommonUtil.isFastDoubleClick()) { - return; - } cmd.executeCmd(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java index 5a486e5f..667598ac 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java @@ -1,25 +1,44 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.arialyy.aria.core.command; import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.task.AbsTask; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.DGroupTaskQueue; import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.NetUtils; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; /** * Created by AriaL on 2017/6/13. @@ -30,7 +49,7 @@ import java.util.List; * 4.恢复下载的任务规则是,停止时间越晚的任务启动越早,按照DESC来进行排序 */ final class ResumeAllCmd extends AbsNormalCmd { - private List mWaitList = new ArrayList<>(); + ResumeAllCmd(T entity, int taskType) { super(entity, taskType); @@ -41,94 +60,8 @@ final class ResumeAllCmd extends AbsNormalCmd { ALog.w(TAG, "恢复任务失败,网络未连接"); return; } - if (isDownloadCmd) { - findTaskData(1); - findTaskData(2); - } else { - findTaskData(3); - } - resumeWaitTask(); - } - - /** - * 查找数据库中的所有任务数据 - * - * @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务 - */ - private void findTaskData(int type) { - if (type == 1) { - List entities = - DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", "1"); - if (entities != null && !entities.isEmpty()) { - for (DownloadEntity entity : entities) { - addResumeEntity(TaskWrapperManager.getInstance() - .getNormalTaskWrapper(DTaskWrapper.class, entity.getId())); - } - } - } else if (type == 2) { - List entities = - DbEntity.findDatas(DownloadGroupEntity.class, "state!=? ORDER BY stopTime DESC", "1"); - if (entities != null && !entities.isEmpty()) { - for (DownloadGroupEntity entity : entities) { - addResumeEntity( - TaskWrapperManager.getInstance() - .getGroupWrapper(DGTaskWrapper.class, entity.getId())); - } - } - } else if (type == 3) { - List entities = - DbEntity.findDatas(UploadEntity.class, "state!=? ORDER BY stopTime DESC", "1"); - if (entities != null && !entities.isEmpty()) { - for (UploadEntity entity : entities) { - addResumeEntity(TaskWrapperManager.getInstance() - .getNormalTaskWrapper(UTaskWrapper.class, entity.getId())); - } - } - } - } - - /** - * 添加恢复实体 - */ - private void addResumeEntity(AbsTaskWrapper te) { - if (te == null || te.getEntity() == null) { - return; - } - if (!mQueue.taskExists(te.getKey())) { - mWaitList.add(te); - } + new Thread(new ResumeThread(isDownloadCmd, IEntity.STATE_COMPLETE)).start(); } - /** - * 处理等待状态的任务 - */ - private void resumeWaitTask() { - int maxTaskNum = mQueue.getMaxTaskNum(); - if (mWaitList == null || mWaitList.isEmpty()) { - return; - } - List resumeEntities = new ArrayList<>(); - for (AbsTaskWrapper te : mWaitList) { - if (te instanceof DTaskWrapper) { - mQueue = DTaskQueue.getInstance(); - } else if (te instanceof UTaskWrapper) { - mQueue = UTaskQueue.getInstance(); - } else if (te instanceof DGTaskWrapper) { - mQueue = DGroupTaskQueue.getInstance(); - } - if (mQueue.getCurrentExePoolNum() < maxTaskNum) { - startTask(createTask(te)); - } else { - te.getEntity().setState(IEntity.STATE_WAIT); - AbsTask task = createTask(te); - sendWaitState(task); - resumeEntities.add(te.getEntity()); - } - } - if (!resumeEntities.isEmpty()) { - DbEntity.updateManyData(resumeEntities); - } - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java new file mode 100644 index 00000000..64efead9 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.command; + +import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.download.DGTaskWrapper; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.inf.IOptionConstant; +import com.arialyy.aria.core.listener.ISchedulers; +import com.arialyy.aria.core.manager.TaskWrapperManager; +import com.arialyy.aria.core.queue.AbsTaskQueue; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; +import com.arialyy.aria.core.queue.UTaskQueue; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.util.ArrayList; +import java.util.List; + +/** + * 恢复任务工具 + */ +public class ResumeThread implements Runnable { + private String TAG = CommonUtil.getClassName(getClass()); + private List mWaitList = new ArrayList<>(); + private boolean isDownloadCmd; + private int excludeState; + + ResumeThread(boolean isDownload, int excludeState) { + this.isDownloadCmd = isDownload; + this.excludeState = excludeState; + } + + /** + * 查找数据库中的所有任务数据 + * + * @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务 + */ + private void findTaskData(int type) { + if (type == 1) { + List entities = + DbEntity.findDatas(DownloadEntity.class, + "isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", + String.valueOf(excludeState)); + if (entities != null && !entities.isEmpty()) { + for (DownloadEntity entity : entities) { + addResumeEntity(TaskWrapperManager.getInstance() + .getNormalTaskWrapper(DTaskWrapper.class, entity.getId())); + } + } + } else if (type == 2) { + List entities = + DbEntity.findDatas(DownloadGroupEntity.class, "state!=? ORDER BY stopTime DESC", + String.valueOf(excludeState)); + if (entities != null && !entities.isEmpty()) { + for (DownloadGroupEntity entity : entities) { + addResumeEntity( + TaskWrapperManager.getInstance() + .getGroupWrapper(DGTaskWrapper.class, entity.getId())); + } + } + } else if (type == 3) { + List entities = + DbEntity.findDatas(UploadEntity.class, "state!=? ORDER BY stopTime DESC", + String.valueOf(excludeState)); + if (entities != null && !entities.isEmpty()) { + for (UploadEntity entity : entities) { + addResumeEntity(TaskWrapperManager.getInstance() + .getNormalTaskWrapper(UTaskWrapper.class, entity.getId())); + } + } + } + } + + /** + * 添加恢复实体 + */ + private void addResumeEntity(AbsTaskWrapper te) { + if (te == null || te.getEntity() == null || TextUtils.isEmpty(te.getKey())) { + return; + } + mWaitList.add(te); + } + + /** + * 处理等待状态的任务 + */ + private void resumeWaitTask() { + + if (mWaitList == null || mWaitList.isEmpty()) { + return; + } + List resumeEntities = new ArrayList<>(); + + for (AbsTaskWrapper wrapper : mWaitList) { + AbsTaskQueue queue = null; + if (wrapper instanceof DTaskWrapper) { + queue = DTaskQueue.getInstance(); + } else if (wrapper instanceof UTaskWrapper) { + queue = UTaskQueue.getInstance(); + } else if (wrapper instanceof DGTaskWrapper) { + queue = DGroupTaskQueue.getInstance(); + } + + if (queue == null){ + ALog.e(TAG, "任务类型错误"); + continue; + } + + if (wrapper.getEntity() == null || TextUtils.isEmpty(wrapper.getKey())) { + ALog.e(TAG, "任务实体为空或key为空"); + continue; + } + + AbsTask task = queue.getTask(wrapper.getKey()); + if (task != null) { + ALog.w(TAG, "任务已存在"); + continue; + } + + int maxTaskNum = queue.getMaxTaskNum(); + task = queue.createTask(wrapper); + if (task == null) { + continue; + } + + handleWrapper(wrapper); + + if (queue.getCurrentExePoolNum() < maxTaskNum) { + queue.startTask(task); + } else { + wrapper.getEntity().setState(IEntity.STATE_WAIT); + sendWaitState(task); + resumeEntities.add(wrapper.getEntity()); + } + } + if (!resumeEntities.isEmpty()) { + DbEntity.updateManyData(resumeEntities); + } + } + + /** + * 处理ftp的wrapper + */ + private void handleWrapper(AbsTaskWrapper wrapper) { + int requestType = wrapper.getRequestType(); + if (requestType == ITaskWrapper.D_FTP + || requestType == ITaskWrapper.U_FTP + || requestType == ITaskWrapper.D_FTP_DIR) { + wrapper.getOptionParams() + .setParams(IOptionConstant.ftpUrlEntity, + CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey())); + } + } + + /** + * 发送等待状态 + */ + private void sendWaitState(AbsTask task) { + if (task != null) { + task.getTaskWrapper().setState(IEntity.STATE_WAIT); + task.getOutHandler().obtainMessage(ISchedulers.WAIT, task).sendToTarget(); + } + } + + @Override public void run() { + if (isDownloadCmd) { + findTaskData(1); + findTaskData(2); + } else { + findTaskData(3); + } + resumeWaitTask(); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java index 8c520bf9..38b1367a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java @@ -18,27 +18,11 @@ package com.arialyy.aria.core.command; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.QueueMod; -import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DGroupTaskQueue; -import com.arialyy.aria.core.queue.DTaskQueue; -import com.arialyy.aria.core.queue.UTaskQueue; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.NetUtils; -import java.util.ArrayList; -import java.util.List; /** * Created by lyy on 2016/8/22. 开始命令 队列模型{@link QueueMod#NOW}、{@link QueueMod#WAIT} @@ -107,75 +91,6 @@ final class StartCmd extends AbsNormalCmd { * 当缓冲队列为null时,查找数据库中所有等待中的任务 */ private void findAllWaitTask() { - new Thread(new WaitTaskThread()).start(); - } - - private class WaitTaskThread implements Runnable { - - @Override public void run() { - if (isDownloadCmd) { - handleTask(findWaitData(1)); - handleTask(findWaitData(2)); - } else { - handleTask(findWaitData(3)); - } - } - - private List findWaitData(int type) { - List waitList = new ArrayList<>(); - TaskWrapperManager tManager = TaskWrapperManager.getInstance(); - if (type == 1) { // 普通下载任务 - List dEntities = DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? and state=?", "false", "3"); - if (dEntities != null && !dEntities.isEmpty()) { - for (DownloadEntity e : dEntities) { - waitList.add(tManager.getNormalTaskWrapper(DTaskWrapper.class, e.getId())); - } - } - } else if (type == 2) { // 组合任务 - List dEntities = - DbEntity.findDatas(DownloadGroupEntity.class, "state=?", "3"); - if (dEntities != null && !dEntities.isEmpty()) { - for (DownloadGroupEntity e : dEntities) { - if (e.getTaskType() == ITaskWrapper.DG_HTTP) { - waitList.add(tManager.getGroupWrapper(DGTaskWrapper.class, e.getId())); - } else if (e.getTaskType() == ITaskWrapper.D_FTP_DIR) { - waitList.add(tManager.getGroupWrapper(DGTaskWrapper.class, e.getId())); - } - } - } - } else if (type == 3) { //普通上传任务 - List dEntities = DbEntity.findDatas(UploadEntity.class, "state=?", "3"); - - if (dEntities != null && !dEntities.isEmpty()) { - for (UploadEntity e : dEntities) { - waitList.add(tManager.getNormalTaskWrapper(UTaskWrapper.class, e.getId())); - } - } - } - return waitList; - } - - private void handleTask(List waitList) { - for (AbsTaskWrapper wrapper : waitList) { - if (wrapper.getEntity() == null) continue; - AbsTask task = getTask(wrapper.getKey()); - if (task != null) continue; - if (wrapper instanceof DTaskWrapper) { - if (wrapper.getRequestType() == ITaskWrapper.D_FTP - || wrapper.getRequestType() == ITaskWrapper.U_FTP) { - wrapper.getOptionParams() - .setParams(IOptionConstant.ftpUrlEntity, - CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey())); - } - mQueue = DTaskQueue.getInstance(); - } else if (wrapper instanceof UTaskWrapper) { - mQueue = UTaskQueue.getInstance(); - } else if (wrapper instanceof DGTaskWrapper) { - mQueue = DGroupTaskQueue.getInstance(); - } - createTask(wrapper); - } - } + new Thread(new ResumeThread(isDownloadCmd, IEntity.STATE_WAIT)).start(); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java index bbf4db72..d66b25a1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java @@ -19,6 +19,7 @@ import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.command.CmdHelper; +import com.arialyy.aria.util.ALog; /** * 创建任务时使用的控制器 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index c8eb2bbf..3f7e149c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -133,18 +133,8 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private boolean checkPathConflicts(String filePath) { //设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径 if (!filePath.equals(mEntity.getFilePath())) { - - //if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { - // if (!mWrapper.isForceDownload()) { - // ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); - // return false; - // } else { - // ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); - // RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD); - // } - //} // 检查路径冲突 - if (!CheckUtil.checkAndHandlePathConflicts(mWrapper.isForceDownload(), filePath)) { + if (!CheckUtil.checkDownloadPathConflicts(mWrapper.isForceDownload(), filePath)) { return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java index efca22d4..4cbf22c9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java @@ -42,14 +42,17 @@ class DGTaskWrapperFactory implements IGroupWrapperFactory { FtpBuilderTarget(String filePath) { mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); - getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); + getTaskWrapper().setRequestType(ITaskWrapper.U_FTP); } /** @@ -48,6 +49,14 @@ public class FtpBuilderTarget extends AbsBuilderTarget { return this; } + /** + * 如果文件路径被其它任务占用,删除其它任务 + */ + public FtpBuilderTarget forceUpload() { + ((UTaskWrapper)getTaskWrapper()).setForceUpload(true); + return this; + } + /** * 设置登陆、字符串编码、ftps等参数 */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index 8b9f946a..e8353f9d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -20,7 +20,7 @@ import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; /** @@ -32,7 +32,7 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new UNormalConfigHandler<>(this, taskId); - getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP); + getTaskWrapper().setRequestType(ITaskWrapper.U_FTP); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index b8b7ea5f..8a72c40c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -19,6 +19,7 @@ import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; +import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -60,4 +61,12 @@ public class HttpBuilderTarget extends AbsBuilderTarget { getTaskWrapper().getOptionParams().setParams(option); return this; } + + /** + * 如果文件路径被其它任务占用,删除其它任务 + */ + public HttpBuilderTarget forceUpload() { + ((UTaskWrapper) getTaskWrapper()).setForceUpload(true); + return this; + } } diff --git a/DEV_LOG.md b/DEV_LOG.md index f487425f..de37719f 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,9 @@ ## 开发日志 + + v_3.7.6 + - 增加强制上传的api`forceUpload()` + - 修复for循环上传文件出现的问题 + - 移除创建任务的500ms间隔限制 + - 修复多线程读写时可能出现的`database is locked`的问题 + v_3.7.5 (2019/11/10) - fix bug https://github.com/AriaLyy/Aria/issues/500 - fix bug https://github.com/AriaLyy/Aria/issues/508 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java index 2d7a7aa1..5bdb7614 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java @@ -74,7 +74,7 @@ public class FtpDirInfoThread extends AbsFtpInfoThread 1 && Configuration.getInstance().downloadCfg.isUseBlock(); - // 线程数为1,或者使用了分块,则认为是使用动态长度文件 - record.isOpenDynamicFile = threadNum == 1 || record.isBlock; + record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock(); } else { record.isBlock = false; } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java index 976d12e4..c5af65ae 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDThreadTaskAdapter.java @@ -83,7 +83,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter { return; } - if (getThreadConfig().isOpenDynamicFile) { + if (getThreadConfig().isBlock) { readDynamicFile(is); } else { readNormal(is); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java index 88daeb7f..21df6087 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java @@ -49,7 +49,7 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { if (record.isBlock) { helper.handleBlockRecord(); } else { - helper.handleMutilRecord(); + helper.handleMultiRecord(); } } else if (!getWrapper().isSupportBP()) { helper.handleNoSupportBPRecord(); @@ -87,9 +87,7 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { int requestType = getWrapper().getRequestType(); if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR || requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) { - record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); - // 线程数为1,或者使用了分块,则认为是使用动态长度文件 - record.isOpenDynamicFile = threadNum == 1 || record.isBlock; + record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock(); } else { record.isBlock = false; } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index e69ca84e..b9cfe3ea 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -103,7 +103,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); if (mTaskOption.isChunked()) { readChunked(is); - } else if (getThreadConfig().isOpenDynamicFile) { + } else if (getThreadConfig().isBlock) { readDynamicFile(is); } else { //创建可设置位置的文件 diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 8ba21d6b..56d4050a 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -56,7 +56,7 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { long currentProgress = 0; int completeNum = 0; File targetFile = new File(mTaskRecord.filePath); - if (!targetFile.exists()){ + if (!targetFile.exists()) { FileUtil.createFile(targetFile); } @@ -117,17 +117,15 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { record.filePath = getEntity().getFilePath(); record.threadRecords = new ArrayList<>(); record.threadNum = threadNum; + record.isBlock = true; int requestType = getWrapper().getRequestType(); if (requestType == ITaskWrapper.M3U8_VOD) { record.taskType = TaskRecord.TYPE_M3U8_VOD; - record.isOpenDynamicFile = true; - record.bandWidth = mOption.getBandWidth(); } else if (requestType == ITaskWrapper.M3U8_LIVE) { record.taskType = TaskRecord.TYPE_M3U8_LIVE; - record.isOpenDynamicFile = true; - record.bandWidth = mOption.getBandWidth(); } + record.bandWidth = mOption.getBandWidth(); return record; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java index 41d86041..39d95a3d 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java @@ -100,7 +100,7 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter { is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); if (mHttpTaskOption.isChunked()) { readChunked(is); - } else if (getThreadConfig().isOpenDynamicFile) { + } else if (getThreadConfig().isBlock) { readDynamicFile(is); } } catch (MalformedURLException e) { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index f474d7dd..09d96c28 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -177,7 +177,6 @@ public class M3U8LiveLoader extends BaseM3U8Loader { config.url = tsUrl; config.tempFile = new File(getTsFilePath(cacheDir, indexId)); config.isBlock = mRecord.isBlock; - config.isOpenDynamicFile = mRecord.isOpenDynamicFile; config.taskWrapper = mTaskWrapper; config.record = record; config.stateHandler = mStateHandler; diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index c88fa5e2..dba71cc0 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -436,7 +436,6 @@ public class M3U8VodLoader extends BaseM3U8Loader { config.url = record.tsUrl; config.tempFile = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); config.isBlock = mRecord.isBlock; - config.isOpenDynamicFile = mRecord.isOpenDynamicFile; config.taskWrapper = mTaskWrapper; config.record = record; config.stateHandler = mStateHandler; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java index 035d7127..222c395b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java @@ -78,12 +78,6 @@ public class TaskRecord extends DbEntity { */ public boolean isBlock = false; - /** - * 是否是使用虚拟文件下载的 - * {@code true}是,{@code false}不是 - */ - public boolean isOpenDynamicFile = false; - /** * 线程类型 * {@link #TYPE_HTTP_FTP}、{@link #TYPE_M3U8_VOD} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index bbdf78bf..5a845409 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -125,7 +125,6 @@ public class RecordHandler implements IRecordHandler { } mTaskWrapper.setNewTask(false); mTaskRecord = mAdapter.createTaskRecord(threadNum); - mTaskRecord.isOpenDynamicFile = false; mTaskRecord.isBlock = false; File tempFile = new File(getFilePath()); for (int i = 0; i < threadNum; i++) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index a6d8d157..f6fc6f97 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -45,13 +45,13 @@ public class RecordHelper { /** * 处理非分块的,多线程任务 */ - public void handleMutilRecord() { + public void handleMultiRecord() { // 默认线程分块长度 long blockSize = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size(); File temp = new File(mTaskRecord.filePath); boolean fileExists = false; if (!temp.exists()) { - BufferedRandomAccessFile tempFile = null; + BufferedRandomAccessFile tempFile; try { tempFile = new BufferedRandomAccessFile(temp, "rw"); tempFile.setLength(mWrapper.getEntity().getFileSize()); @@ -145,10 +145,10 @@ public class RecordHelper { tr.startLocation = 0; tr.isComplete = false; tr.endLocation = mWrapper.getEntity().getFileSize(); - } else if (mTaskRecord.isOpenDynamicFile) { + } else if (mTaskRecord.isBlock) { if (file.length() > mWrapper.getEntity().getFileSize()) { ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); - file.delete(); + FileUtil.deleteFile(file); tr.startLocation = 0; tr.isComplete = false; tr.endLocation = mWrapper.getEntity().getFileSize(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index 3964916d..46a351ca 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -35,8 +35,6 @@ public class SubThreadConfig { public ThreadRecord record; // 状态处理器 public Handler stateHandler; - // 动态文件 - public boolean isOpenDynamicFile; // m3u8切片索引 public int peerIndex; } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index ce95a3e9..a6b3c0ef 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download; import android.os.Parcel; +import android.text.TextUtils; import com.arialyy.aria.core.common.AbsGroupEntity; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Ignore; @@ -48,7 +49,12 @@ public class DownloadGroupEntity extends AbsGroupEntity { } @Override public int getTaskType() { - return getKey().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR : ITaskWrapper.DG_HTTP; + if (getSubEntities() == null || getSubEntities().isEmpty() || TextUtils.isEmpty( + getSubEntities().get(0).getUrl())) { + return ITaskWrapper.ERROR; + } + return getSubEntities().get(0).getUrl().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR + : ITaskWrapper.DG_HTTP; } public DownloadGroupEntity() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index e778b5f6..6b113f2e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -142,7 +142,6 @@ public class NormalLoader extends AbsLoader { String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) : mTempFile; config.isBlock = mRecord.isBlock; - config.isOpenDynamicFile = mRecord.isOpenDynamicFile; config.startThreadNum = startNum; config.taskWrapper = mTaskWrapper; config.record = record; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 23df702b..7dd1c54d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -463,8 +463,6 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { if (mRecord != null) { mRecord.isComplete = isComplete; if (mConfig.isBlock) { - mRecord.startLocation = record; - } else if (mConfig.isOpenDynamicFile) { mRecord.startLocation = mConfig.tempFile.length(); } else { if (0 < record && record < mRecord.endLocation) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java index 42e5d4c8..5ab218d8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java @@ -29,6 +29,11 @@ public class UTaskWrapper extends AbsTaskWrapper { */ private String tempUrl; + /** + * 如果文件路径被其它任务占用,设置为true时,删除其它任务 + */ + private boolean forceUpload = false; + public UTaskWrapper(UploadEntity entity) { super(entity); } @@ -48,6 +53,14 @@ public class UTaskWrapper extends AbsTaskWrapper { return getEntity().getKey(); } + public boolean isForceUpload() { + return forceUpload; + } + + public void setForceUpload(boolean forceUpload) { + this.forceUpload = forceUpload; + } + @Override public UploadConfig getConfig() { return Configuration.getInstance().uploadCfg; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index f7d74df9..27db92a2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.upload; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Primary; @@ -58,7 +59,10 @@ public class UploadEntity extends AbsNormalEntity implements Parcelable { } @Override public int getTaskType() { - return getUrl().startsWith("ftp") ? ITaskWrapper.D_FTP : ITaskWrapper.D_HTTP; + if (TextUtils.isEmpty(getUrl())){ + return ITaskWrapper.ERROR; + } + return getUrl().startsWith("ftp") ? ITaskWrapper.U_FTP : ITaskWrapper.U_HTTP; } public UploadEntity() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index a2b283e9..3e489475 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -24,6 +24,7 @@ import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ComponentUtil; /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java index d74f02dc..43d1e03c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -34,7 +34,7 @@ class DBConfig { static boolean DEBUG = false; static Map mapping = new LinkedHashMap<>(); static String DB_NAME; - static int VERSION = 55; + static int VERSION = 56; /** * 是否将数据库保存在Sd卡,{@code true} 是 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 4b2683db..16ae1bcb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -76,9 +76,6 @@ final class SqlHelper extends SQLiteOpenHelper { // 需要使用如下语句: db.execSQL("PRAGMA foreign_keys=ON;"); } - if (DBConfig.DEBUG) { - db.enableWriteAheadLogging(); - } } @Override public void onCreate(SQLiteDatabase db) { @@ -139,6 +136,7 @@ final class SqlHelper extends SQLiteOpenHelper { SQLiteDatabase.CREATE_IF_NECESSARY); } } + db.enableWriteAheadLogging(); return db; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 28915e3e..c8874bc1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -19,6 +19,7 @@ package com.arialyy.aria.util; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; import java.lang.reflect.Modifier; import java.util.List; @@ -37,7 +38,7 @@ public class CheckUtil { * @param filePath 文件保存路径 * @return false 任务不再执行,true 任务继续执行 */ - public static boolean checkAndHandlePathConflicts(boolean isForceDownload, String filePath) { + public static boolean checkDownloadPathConflicts(boolean isForceDownload, String filePath) { if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { if (!isForceDownload) { ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); @@ -51,6 +52,27 @@ public class CheckUtil { return true; } + /** + * 检查和处理路径冲突 + * + * @param isForceUpload true,如果路径冲突,将删除其它任务的记录的 + * @param filePath 文件保存路径 + * @return false 任务不再执行,true 任务继续执行 + */ + public static boolean checkUploadPathConflicts(boolean isForceUpload, String filePath) { + if (DbEntity.checkDataExist(UploadEntity.class, "filePath=?", filePath)) { + if (!isForceUpload) { + ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); + return false; + } else { + ALog.w(TAG, String.format("文件路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); + RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_UPLOAD); + return true; + } + } + return true; + } + /** * 检查成员类是否是静态和public */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 5ea85006..080e92f4 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -101,6 +101,9 @@ public class ComponentUtil { public T buildUtil(AbsTaskWrapper wrapper, IEventListener listener) { int requestType = wrapper.getRequestType(); String className = null; + if (requestType == 1){ + throw new IllegalArgumentException("xxxx"); + } switch (requestType) { case ITaskWrapper.M3U8_LIVE: className = "com.arialyy.aria.m3u8.live.M3U8LiveUtil"; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java index c0fb0f11..7c845edc 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ErrorHelp.java @@ -50,17 +50,7 @@ public class ErrorHelp { CommonUtil.getAppPath(AriaConfig.getInstance().getAPP()), getData("yyyy-MM-dd_HH_mm_ss")); - File log = new File(path); - if (!log.getParentFile().exists()) { - log.getParentFile().mkdirs(); - } - if (!log.exists()) { - try { - log.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } + FileUtil.createFile(path); return path; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 5ce2fba6..ceddf62c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -128,7 +128,7 @@ public class RecordUtil { filePath = ((DownloadEntity) entity).getDownloadPath(); } else if (entity instanceof UploadEntity) { type = IRecordHandler.TYPE_UPLOAD; - filePath = ((UploadEntity) entity).getFilePath(); + filePath = entity.getFilePath(); } else { ALog.w(TAG, "删除记录失败,未知类型"); return; diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index b5fcdb5c..3bf0637a 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -34,6 +34,7 @@ public class HttpDownloadModule extends BaseViewModule { private final String defUrl = "http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk"; + //"https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; //"http://9.9.9.205:5000/download/Cyberduck-6.9.4.30164.zip"; //"http://202.98.201.103:7000/vrs/TPK/ZTC440402001Z.tpk"; private final String defFilePath = @@ -51,6 +52,7 @@ public class HttpDownloadModule extends BaseViewModule { //String url = // "http://sdkdown.muzhiwan.com/openfile/2019/05/21/com.netease.tom.mzw_5ce3ef8754d05.apk"; String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; + //String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); diff --git a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java index 07bf5126..5d8fc54c 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java @@ -18,6 +18,7 @@ package com.arialyy.simple.core.upload; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Environment; import android.util.Log; import android.view.View; import androidx.annotation.Nullable; @@ -40,6 +41,8 @@ import com.arialyy.simple.databinding.ActivityFtpUploadBinding; import com.arialyy.simple.util.AppUtil; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * Created by lyy on 2017/7/28. Ftp 文件上传demo @@ -111,26 +114,26 @@ public class FtpUploadActivity extends BaseActivity { public void onClick(View view) { switch (view.getId()) { case R.id.start: - if (mTaskId == -1) { - mTaskId = Aria.upload(this) - .loadFtp(mFilePath) - .setUploadUrl(mUrl) - .option(getOption()) - .create(); - getBinding().setStateStr(getString(R.string.stop)); - break; - } - if (Aria.upload(this).loadFtp(mTaskId).isRunning()) { - Aria.upload(this).loadFtp(mTaskId).stop(); - getBinding().setStateStr(getString(R.string.resume)); - } else { - Aria.upload(this) - .loadFtp(mTaskId) - .option(getOption()) - .resume(); - getBinding().setStateStr(getString(R.string.stop)); - } - + //if (mTaskId == -1) { + // mTaskId = Aria.upload(this) + // .loadFtp(mFilePath) + // .setUploadUrl(mUrl) + // .option(getOption()) + // .create(); + // getBinding().setStateStr(getString(R.string.stop)); + // break; + //} + //if (Aria.upload(this).loadFtp(mTaskId).isRunning()) { + // Aria.upload(this).loadFtp(mTaskId).stop(); + // getBinding().setStateStr(getString(R.string.resume)); + //} else { + // Aria.upload(this) + // .loadFtp(mTaskId) + // .option(getOption()) + // .resume(); + // getBinding().setStateStr(getString(R.string.stop)); + //} + upload(); break; case R.id.cancel: Aria.upload(this).loadFtp(mTaskId).cancel(); @@ -140,6 +143,28 @@ public class FtpUploadActivity extends BaseActivity { } } + private void upload() { + List paths = new ArrayList<>(); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/1.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/2.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/3.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/4.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/5.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/6.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/7.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/8.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/9.jpg"); + paths.add(Environment.getExternalStorageDirectory().getPath() + "/Download/img/img/10.jpg"); + for (String path : paths) { + Aria.upload(this) + .loadFtp(path) + .setUploadUrl(mUrl) + .option(getOption()) + .forceUpload() + .create(); + } + } + private FtpOption getOption() { FtpOption option = new FtpOption(); option.login(user, pwd); From 1b54bfc4ac0b9a2b766a832faf67eefd287c3b96 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Tue, 19 Nov 2019 20:03:25 +0800 Subject: [PATCH 021/140] 3.7.6 --- .../aria/core/download/CheckDGEntityUtil.java | 5 +- .../core/upload/target/FtpBuilderTarget.java | 1 + DEV_LOG.md | 5 +- .../aria/ftp/upload/FtpUFileInfoThread.java | 1 + README.md | 33 ++++++----- .../aria/sftp/AbsSFtpFileInfoThread.java | 58 ------------------- .../aria/sftp/BaseInfoThreadAdapter.java | 42 ++++++++++++++ ...FtpInfoThread.java => SFtpInfoThread.java} | 50 ++++++++++++++-- .../sftp/download/DSFtpInfoThreadAdapter.java | 31 ++++++++++ .../aria/sftp/download/SFtpDLoaderUtil.java | 20 ++++--- .../download/group/DownloadGroupActivity.java | 8 +-- .../core/download/group/GroupModule.java | 6 +- .../simple/core/upload/FtpUploadActivity.java | 53 ++++++++++------- .../simple/core/upload/UploadModule.java | 2 +- build.gradle | 4 +- 15 files changed, 201 insertions(+), 118 deletions(-) delete mode 100644 SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java create mode 100644 SFtpComponent/src/main/java/com/arialyy/aria/sftp/BaseInfoThreadAdapter.java rename SFtpComponent/src/main/java/com/arialyy/aria/sftp/{AbsSFtpInfoThread.java => SFtpInfoThread.java} (53%) create mode 100644 SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/DSFtpInfoThreadAdapter.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 5af68983..402b1846 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -170,9 +170,8 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { if (!newName.equals(entity.getFileName())) { String oldPath = mEntity.getDirPath() + "/" + entity.getFileName(); String newPath = mEntity.getDirPath() + "/" + newName; - if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=? or isComplete='true'", - newPath)) { - ALog.w(TAG, String.format("更新文件名失败,路径【%s】已存在或文件已下载", newPath)); + if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", newPath)) { + ALog.w(TAG, String.format("更新文件名失败,路径【%s】被其它任务占用", newPath)); return; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index 6d91f33f..741dd2d8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -35,6 +35,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); getTaskWrapper().setRequestType(ITaskWrapper.U_FTP); + getTaskWrapper().setNewTask(true); } /** diff --git a/DEV_LOG.md b/DEV_LOG.md index de37719f..fa4f7b3d 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,5 +1,8 @@ ## 开发日志 - + v_3.7.6 + + v_3.7.6 (2019/11/19) + - fix bug https://github.com/AriaLyy/Aria/issues/505 + - fix bug https://github.com/AriaLyy/Aria/issues/516 + - fix bug https://github.com/AriaLyy/Aria/issues/515 - 增加强制上传的api`forceUpload()` - 修复for循环上传文件出现的问题 - 移除创建任务的500ms间隔限制 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java index e13a174e..29f1cdc7 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java @@ -60,6 +60,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { @Override protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) { // 旧任务将不做处理,否则断点续传上传将失效 if (!mTaskWrapper.isNewTask()) { + ALog.d(TAG, "任务是旧任务,忽略该拦截器"); return true; } try { diff --git a/README.md b/README.md index a1adfe46..8b31e914 100644 --- a/README.md +++ b/README.md @@ -44,20 +44,20 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.5-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.5-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.5-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.5-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.6-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.6-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.6-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.6-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.5' -annotationProcessor 'com.arialyy.aria:compiler:3.7.5' -implementation 'com.arialyy.aria:ftpComponent:3.7.5' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.5' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.6' +annotationProcessor 'com.arialyy.aria:compiler:3.7.6' +implementation 'com.arialyy.aria:ftpComponent:3.7.6' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.6' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` -api('com.arialyy.aria:aria-core:'){ +api('com.arialyy.aria:core:'){ exclude group: 'androidx.appcompat.app' } ``` @@ -65,7 +65,7 @@ api('com.arialyy.aria:aria-core:'){ __⚠️注意:3.5.4以下版本升级时,需要更新[配置文件](https://aria.laoyuyu.me/aria_doc/start/config.html)!!__ -__⚠️注意:3.7 以上版本已经适配了AndroidX__ +__⚠️注意:3.7 以上版本已经适配了AndroidX,如果是使用support库的,可使用[老版本](https://github.com/AriaLyy/Aria/tree/v3.6.6)__ *** ## 使用 @@ -137,11 +137,14 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 -+ v_3.7.5 (2019/11/10) - - fix bug https://github.com/AriaLyy/Aria/issues/500 - - fix bug https://github.com/AriaLyy/Aria/issues/508 - - fix bug https://github.com/AriaLyy/Aria/issues/503 - - 修复m3u8创建索引不成功的问题 + + v_3.7.6 (2019/11/19) + - fix bug https://github.com/AriaLyy/Aria/issues/505 + - fix bug https://github.com/AriaLyy/Aria/issues/516 + - fix bug https://github.com/AriaLyy/Aria/issues/515 + - 增加强制上传的api`forceUpload()` + - 修复for循环上传文件出现的问题 + - 移除创建任务的500ms间隔限制 + - 修复多线程读写时可能出现的`database is locked`的问题 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java deleted file mode 100644 index 858cf640..00000000 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.sftp; - -import com.arialyy.aria.core.inf.OnFileInfoCallback; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.jcraft.jsch.ChannelSftp; -import com.jcraft.jsch.JSchException; - -/** - * 获取sftp文件信息 - * - * @author lyy - */ -public class AbsSFtpFileInfoThread implements Runnable { - - private WP mWrapper; - private SFtpUtil mUtil; - private OnFileInfoCallback mCallback; - - public AbsSFtpFileInfoThread(SFtpUtil util, WP wrapper, OnFileInfoCallback callback) { - mWrapper = wrapper; - mUtil = util; - mCallback = callback; - } - - @Override public void run() { - startFlow(); - } - - private void startFlow() { - - } - - private ChannelSftp createChannel() { - ChannelSftp sftp = null; - try { - sftp = (ChannelSftp) mUtil.getSession().openChannel(SFtpUtil.CMD_TYPE_SFTP); - sftp.connect(); - } catch (JSchException e) { - e.printStackTrace(); - } - return sftp; - } -} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/BaseInfoThreadAdapter.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/BaseInfoThreadAdapter.java new file mode 100644 index 00000000..53a939c3 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/BaseInfoThreadAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.sftp; + +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import java.util.Vector; + +/** + * sftp文件信息适配器 + */ +public abstract class BaseInfoThreadAdapter { + + private WRAPPER mWrapper; + + public BaseInfoThreadAdapter(WRAPPER taskWrapper) { + mWrapper = taskWrapper; + } + + public WRAPPER getWrapper() { + return mWrapper; + } + + /** + * 处理文件 + * + * @return true 处理文件成功,false 处理文件失败,结束任务 + */ + protected abstract boolean handlerFile(Vector vector); +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpInfoThread.java similarity index 53% rename from SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java rename to SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpInfoThread.java index 3ab60bf6..cf517381 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpInfoThread.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpInfoThread.java @@ -16,19 +16,25 @@ package com.arialyy.aria.sftp; import com.arialyy.aria.core.AriaConfig; -import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.FtpUrlEntity; +import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.ftp.FtpTaskOption; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.SftpException; +import java.util.Vector; /** - * * https://cloud.tencent.com/developer/article/1354612 + * * @author lyy */ -public class AbsSFtpInfoThread> +public class SFtpInfoThread> implements Runnable { private final String TAG = CommonUtil.getClassName(getClass()); @@ -40,8 +46,12 @@ public class AbsSFtpInfoThread { + + DSFtpInfoThreadAdapter(DTaskWrapper taskWrapper) { + super(taskWrapper); + } + + @Override protected boolean handlerFile(Vector vector) { + return false; + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java index 5b691cf9..8191cd7d 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java @@ -26,7 +26,7 @@ import com.arialyy.aria.core.loader.NormalLoader; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.ftp.FtpTaskOption; -import com.arialyy.aria.sftp.AbsSFtpFileInfoThread; +import com.arialyy.aria.sftp.SFtpInfoThread; import com.arialyy.aria.sftp.SFtpUtil; /** @@ -57,15 +57,19 @@ public class SFtpDLoaderUtil extends AbsNormalLoaderUtil { } @Override protected Runnable createInfoThread() { - return new AbsSFtpFileInfoThread<>(mSftpUtil, (DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { - @Override public void onComplete(String key, CompleteInfo info) { + DSFtpInfoThreadAdapter adapter = new DSFtpInfoThreadAdapter((DTaskWrapper) getTaskWrapper()); + SFtpInfoThread infoThread = new SFtpInfoThread<>(mSftpUtil, (DTaskWrapper) getTaskWrapper(), + new OnFileInfoCallback() { + @Override public void onComplete(String key, CompleteInfo info) { - } + } - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - mSftpUtil.logout(); - } - }); + } + }); + infoThread.setAdapter(adapter); + + return infoThread; } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index def2c8af..a1446e59 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -95,13 +95,13 @@ public class DownloadGroupActivity extends BaseActivity getUrls() { List urls = new ArrayList<>(); - String[] str = getContext().getResources().getStringArray(R.array.group_urls_1); - Collections.addAll(urls, str); + urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924275&di=a7ea1497a4a2528a45ce7103bf61adf4&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg"); + urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924272&di=7861479e7cb3cea98d585eaf108f056f&imgtype=0&src=http%3A%2F%2Fd.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F562c11dfa9ec8a13c0741f5afd03918fa0ecc01a.jpg"); + //String[] str = getContext().getResources().getStringArray(R.array.group_urls_1); + //Collections.addAll(urls, str); return urls; } diff --git a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java index 5d8fc54c..2fb5db4e 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java @@ -28,6 +28,8 @@ import com.arialyy.annotations.Upload; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.inf.IEntity; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.util.ALog; @@ -114,26 +116,27 @@ public class FtpUploadActivity extends BaseActivity { public void onClick(View view) { switch (view.getId()) { case R.id.start: - //if (mTaskId == -1) { - // mTaskId = Aria.upload(this) - // .loadFtp(mFilePath) - // .setUploadUrl(mUrl) - // .option(getOption()) - // .create(); - // getBinding().setStateStr(getString(R.string.stop)); - // break; - //} - //if (Aria.upload(this).loadFtp(mTaskId).isRunning()) { - // Aria.upload(this).loadFtp(mTaskId).stop(); - // getBinding().setStateStr(getString(R.string.resume)); - //} else { - // Aria.upload(this) - // .loadFtp(mTaskId) - // .option(getOption()) - // .resume(); - // getBinding().setStateStr(getString(R.string.stop)); - //} - upload(); + if (mTaskId == -1) { + mTaskId = Aria.upload(this) + .loadFtp(mFilePath) + .setUploadUrl(mUrl) + .option(getOption()) + .forceUpload() + .create(); + getBinding().setStateStr(getString(R.string.stop)); + break; + } + if (Aria.upload(this).loadFtp(mTaskId).isRunning()) { + Aria.upload(this).loadFtp(mTaskId).stop(); + getBinding().setStateStr(getString(R.string.resume)); + } else { + Aria.upload(this) + .loadFtp(mTaskId) + .option(getOption()) + .resume(); + getBinding().setStateStr(getString(R.string.stop)); + } + //upload(); break; case R.id.cancel: Aria.upload(this).loadFtp(mTaskId).cancel(); @@ -168,6 +171,7 @@ public class FtpUploadActivity extends BaseActivity { private FtpOption getOption() { FtpOption option = new FtpOption(); option.login(user, pwd); + option.setUploadInterceptor(new FtpUploadInterceptor()); return option; } @@ -236,4 +240,13 @@ public class FtpUploadActivity extends BaseActivity { } } } + + private static class FtpUploadInterceptor implements IFtpUploadInterceptor { + @Override public FtpInterceptHandler onIntercept(UploadEntity entity, List fileList) { + FtpInterceptHandler.Builder builder = new FtpInterceptHandler.Builder(); + //builder.coverServerFile(); // 覆盖远端同名文件 + builder.resetFileName("test.zip"); //修改上传到远端服务器的文件名 + return builder.build(); + } + } } diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index e88d07cc..19042e5f 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -37,7 +37,7 @@ public class UploadModule extends BaseViewModule { LiveData getFtpInfo(Context context) { String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.50:2121/aa/你好"); String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, - Environment.getExternalStorageDirectory().getPath() + "/Download/PaNTFS15562.zip"); + Environment.getExternalStorageDirectory().getPath() + "/Download/Folx P58.zip"); UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath); if (entity != null) { diff --git a/build.gradle b/build.gradle index 4ba0e57e..5d899125 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 375 - versionName = '3.7.5' + versionCode = 376 + versionName = '3.7.6' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 0765ab67f7d5f89fba51b9326b2fa59b5a48deb2 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 20 Nov 2019 10:17:08 +0800 Subject: [PATCH 022/140] 3.7.7 --- DEV_LOG.md | 3 +++ .../aria/core/task/AbsThreadTaskAdapter.java | 7 +---- .../aria/core/task/IThreadTaskObserver.java | 5 ++++ .../arialyy/aria/core/task/ThreadTask.java | 5 ++++ .../com/arialyy/aria/util/ComponentUtil.java | 3 --- README.md | 27 ++++++++----------- build.gradle | 4 +-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/DEV_LOG.md b/DEV_LOG.md index fa4f7b3d..cc6b7ec2 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,7 @@ ## 开发日志 + + v_3.7.7 (2019/11/20) + - 修复ftp无法完成下载的问题 + - 修复一个http下载崩溃的问题 + v_3.7.6 (2019/11/19) - fix bug https://github.com/AriaLyy/Aria/issues/505 - fix bug https://github.com/AriaLyy/Aria/issues/516 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java index 4869426c..32593444 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java @@ -34,10 +34,6 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { * 速度限制工具 */ protected BandwidthLimiter mSpeedBandUtil; - /** - * 当前线程的下去区间的进度 - */ - private long mRangeProgress; private ThreadRecord mThreadRecord; private IThreadTaskObserver mObserver; private AbsTaskWrapper mWrapper; @@ -48,7 +44,6 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { mThreadRecord = config.record; mWrapper = config.taskWrapper; mThreadConfig = config; - mRangeProgress = mThreadRecord.startLocation; if (getTaskConfig().getMaxSpeed() > 0) { mSpeedBandUtil = new BandwidthLimiter(getTaskConfig().getMaxSpeed(), config.startThreadNum); } @@ -68,7 +63,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { * 当前线程的下去区间的进度 */ protected long getRangeProgress() { - return mRangeProgress; + return mObserver.getThreadProgress(); } protected ThreadRecord getThreadRecord() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java index 8a92bcf3..1b9b6016 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java @@ -53,4 +53,9 @@ public interface IThreadTaskObserver { * @param len 新增的长度 */ void updateProgress(long len); + + /** + * 获取线程当前进度 + */ + long getThreadProgress(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 7dd1c54d..9a62b7f6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -318,6 +318,11 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } } + @Override + public long getThreadProgress() { + return mRangeProgress; + } + /** * 取消任务 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 080e92f4..5ea85006 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -101,9 +101,6 @@ public class ComponentUtil { public T buildUtil(AbsTaskWrapper wrapper, IEventListener listener) { int requestType = wrapper.getRequestType(); String className = null; - if (requestType == 1){ - throw new IllegalArgumentException("xxxx"); - } switch (requestType) { case ITaskWrapper.M3U8_LIVE: className = "com.arialyy.aria.m3u8.live.M3U8LiveUtil"; diff --git a/README.md b/README.md index 8b31e914..ee2e70ce 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.6-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.6-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.6-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.6-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.7-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.7-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.7-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.7-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.6' -annotationProcessor 'com.arialyy.aria:compiler:3.7.6' -implementation 'com.arialyy.aria:ftpComponent:3.7.6' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.6' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.7' +annotationProcessor 'com.arialyy.aria:compiler:3.7.7' +implementation 'com.arialyy.aria:ftpComponent:3.7.7' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.7' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,14 +137,9 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.6 (2019/11/19) - - fix bug https://github.com/AriaLyy/Aria/issues/505 - - fix bug https://github.com/AriaLyy/Aria/issues/516 - - fix bug https://github.com/AriaLyy/Aria/issues/515 - - 增加强制上传的api`forceUpload()` - - 修复for循环上传文件出现的问题 - - 移除创建任务的500ms间隔限制 - - 修复多线程读写时可能出现的`database is locked`的问题 + + v_3.7.7 (2019/11/20) + - 修复ftp无法完成下载的问题 + - 修复一个http下载崩溃的问题 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/build.gradle b/build.gradle index 5d899125..59427992 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 376 - versionName = '3.7.6' + versionCode = 377 + versionName = '3.7.7' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 1ee36e0bc1ec9cf3dfe318abbc1c32390dd4a08a Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 20 Nov 2019 21:33:48 +0800 Subject: [PATCH 023/140] fix bug https://github.com/AriaLyy/Aria/issues/526 --- DEV_LOG.md | 2 ++ .../aria/ftp/download/FtpDirDLoaderUtil.java | 2 ++ .../aria/http/download/DGroupLoaderUtil.java | 1 + .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 8 +++--- .../arialyy/aria/core/group/AbsGroupUtil.java | 3 +-- .../aria/core/group/AbsSubDLoadUtil.java | 26 ------------------- .../aria/core/group/GroupRunState.java | 8 +++--- README.md | 21 +++++++-------- SFtpComponent/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 1 + .../core/download/HttpDownloadModule.java | 5 ++-- .../core/download/group/GroupModule.java | 8 +++--- .../download/m3u8/M3U8VodDLoadActivity.java | 7 +++-- .../core/download/m3u8/M3U8VodModule.java | 5 ++-- build.gradle | 4 +-- 15 files changed, 44 insertions(+), 59 deletions(-) diff --git a/DEV_LOG.md b/DEV_LOG.md index cc6b7ec2..6d99e34d 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,6 @@ ## 开发日志 + + v_3.7.8-pre-1(2019/11/20) + - fix bug https://github.com/AriaLyy/Aria/issues/526 + v_3.7.7 (2019/11/20) - 修复ftp无法完成下载的问题 - 修复一个http下载崩溃的问题 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java index 57c2675f..9344f982 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java @@ -85,6 +85,8 @@ public class FtpDirDLoaderUtil extends AbsGroupUtil { * @param needCloneInfo 第一次下载,信息已经在{@link FtpDirInfoThread}中clone了 */ private void startDownload(boolean needCloneInfo) { + // ftp需要获取完成只任务信息才更新只任务数量 + getState().setSubSize(getWrapper().getSubTaskWrapper().size()); try { LOCK.lock(); condition.signalAll(); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index b4dc143e..fc66b55a 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -73,6 +73,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil { @Override protected boolean onStart() { super.onStart(); + getState().setSubSize(getWrapper().getSubTaskWrapper().size()); if (getState().getCompleteNum() == getState().getSubSize()) { mListener.onComplete(); } else { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index dba71cc0..86a2ceb5 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -463,7 +463,8 @@ public class M3U8VodLoader extends BaseM3U8Loader { private int cancelNum = 0; // 已经取消的线程的数 private int stopNum = 0; // 已经停止的线程数 private int failNum = 0; // 失败的线程数 - private long progress; //当前总进度,百分比进度 + private long percent; //当前总进度,百分比进度 + private long progress; private TaskRecord taskRecord; // 任务记录 private Looper looper; @@ -480,6 +481,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } } this.listener = listener; + progress = getEntity().getCurrentProgress(); } private void updateStateCount() { @@ -586,7 +588,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } break; case STATE_RUNNING: - //progress += (long) msg.obj; + progress += (long) msg.obj; break; } return true; @@ -610,7 +612,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { int percent = completeNum * 100 / taskRecord.threadRecords.size(); getEntity().setPercent(percent); getEntity().update(); - progress = percent; + this.percent = percent; } @Override public boolean isFail() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index 0b1999a8..12e8bdce 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -81,8 +81,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { * 初始化组合任务状态 */ private void initState() { - mState = new GroupRunState(getWrapper().getKey(), mListener, - mGTWrapper.getSubTaskWrapper().size(), mSubQueue); + mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue); for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) { mState.updateCompleteNum(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java index 2de7fad6..359c554a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java @@ -112,30 +112,4 @@ public abstract class AbsSubDLoadUtil implements IUtil { mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget(); } } - - //@Override public void start() { - // if (mWrapper.getRequestType() == ITaskWrapper.D_HTTP) { - // if (needGetInfo) { - // new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() { - // - // @Override public void onComplete(String url, CompleteInfo info) { - // mDLoader = new Downloader(mListener, mWrapper); - // mDLoader.start(); - // } - // - // @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - // mSchedulers.obtainMessage(ISchedulers.FAIL, SubDLoadUtil.this).sendToTarget(); - // } - // })).start(); - // } else { - // mDLoader = new Downloader(mListener, mWrapper); - // mDLoader.start(); - // } - // } else if (mWrapper.getRequestType() == ITaskWrapper.D_FTP) { - // mDLoader = new Downloader(mListener, mWrapper); - // mDLoader.start(); - // } else { - // ALog.w(TAG, String.format("不识别的类型,requestType:%s", mWrapper.getRequestType())); - // } - //} } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java index 8d68a9a8..dee9834f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java @@ -72,14 +72,16 @@ public class GroupRunState { private String mGroupHash; - GroupRunState(String groupHash, IDGroupListener listener, int subSize, - SimpleSubQueue queue) { + GroupRunState(String groupHash, IDGroupListener listener, SimpleSubQueue queue) { this.listener = listener; this.queue = queue; - mSubSize = subSize; mGroupHash = groupHash; } + public void setSubSize(int subSize){ + mSubSize = subSize; + } + /** * 组合任务是否正在自行 * diff --git a/README.md b/README.md index ee2e70ce..9625c201 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.7-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.7-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.7-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.7-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-v_3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-v_3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-v_3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-v_3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.7' -annotationProcessor 'com.arialyy.aria:compiler:3.7.7' -implementation 'com.arialyy.aria:ftpComponent:3.7.7' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.7' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:v_3.7.8-pre-1' +annotationProcessor 'com.arialyy.aria:compiler:v_3.7.8-pre-1' +implementation 'com.arialyy.aria:ftpComponent:v_3.7.8-pre-1' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:v_3.7.8-pre-1' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,9 +137,8 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.7 (2019/11/20) - - 修复ftp无法完成下载的问题 - - 修复一个http下载崩溃的问题 + + v_v_3.7.8-pre-1 (2019/11/20) + - fix bug https://github.com/AriaLyy/Aria/issues/526 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/SFtpComponent/build.gradle b/SFtpComponent/build.gradle index 193b7be6..9aa5c8cd 100644 --- a/SFtpComponent/build.gradle +++ b/SFtpComponent/build.gradle @@ -29,5 +29,5 @@ dependencies { implementation "com.jcraft:jsch:0.1.55" implementation "com.jcraft:jzlib:1.1.3" implementation project(path: ':FtpComponent') - compile project(path: ':PublicComponent') + implementation project(path: ':PublicComponent') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b577538a..058ff739 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index 3bf0637a..ba978f03 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -39,7 +39,7 @@ public class HttpDownloadModule extends BaseViewModule { //"http://202.98.201.103:7000/vrs/TPK/ZTC440402001Z.tpk"; private final String defFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() - + "/TOTWOO-v3.5.6.apk"; + + "/tttt.apk"; private MutableLiveData liveData = new MutableLiveData<>(); private DownloadEntity singDownloadInfo; @@ -51,7 +51,8 @@ public class HttpDownloadModule extends BaseViewModule { //String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl); //String url = // "http://sdkdown.muzhiwan.com/openfile/2019/05/21/com.netease.tom.mzw_5ce3ef8754d05.apk"; - String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; + //String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; + String url = "https://imtt.dd.qq.com/16891/apk/70BFFDB05AB8686F2A4CF3E07588A377.apk?fsname=com.tencent.tmgp.speedmobile_1.16.0.33877_1160033877.apk&csr=1bbd"; //String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java b/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java index ae01bf21..d35708f4 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java @@ -32,10 +32,10 @@ public class GroupModule extends BaseModule { public List getUrls() { List urls = new ArrayList<>(); - urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924275&di=a7ea1497a4a2528a45ce7103bf61adf4&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg"); - urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924272&di=7861479e7cb3cea98d585eaf108f056f&imgtype=0&src=http%3A%2F%2Fd.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F562c11dfa9ec8a13c0741f5afd03918fa0ecc01a.jpg"); - //String[] str = getContext().getResources().getStringArray(R.array.group_urls_1); - //Collections.addAll(urls, str); + //urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924275&di=a7ea1497a4a2528a45ce7103bf61adf4&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg"); + //urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574141924272&di=7861479e7cb3cea98d585eaf108f056f&imgtype=0&src=http%3A%2F%2Fd.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F562c11dfa9ec8a13c0741f5afd03918fa0ecc01a.jpg"); + String[] str = getContext().getResources().getStringArray(R.array.group_urls_1); + Collections.addAll(urls, str); return urls; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index 3f27fcfd..9a3eaf09 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -308,7 +308,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { //.merge(true) .setVodTsUrlConvert(new VodTsUrlConverter()); //.setMergeHandler(new TsMergeHandler()); - //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); + option.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; } @@ -324,7 +324,10 @@ public class M3U8VodDLoadActivity extends BaseActivity { static class VodTsUrlConverter implements IVodTsUrlConverter { @Override public List convert(String m3u8Url, List tsUrls) { Uri uri = Uri.parse(m3u8Url); - String parentUrl = "http://" + uri.getHost(); + //String parentUrl = "http://" + uri.getHost() + "/gear1/"; + int index = m3u8Url.lastIndexOf("/"); + String parentUrl = m3u8Url.substring(0, index + 1); + //String parentUrl = "http://" + uri.getHost() + "/"; List newUrls = new ArrayList<>(); for (String url : tsUrls) { newUrls.add(parentUrl + url); diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java index 0a9c37cf..979ea085 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java @@ -34,9 +34,8 @@ public class M3U8VodModule extends BaseViewModule { // m3u8测试集合:http://www.voidcn.com/article/p-snaliarm-ct.html //private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8"; // 多码率地址: - //private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; - //private final String defUrl = "http://cn1.fa1244.cn/hls/20190516/6d271eaa73b2e4cb51d13831b0c1ab4c/1557976262/index.m3u8"; - private final String defUrl = "http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8"; + private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; + //private final String defUrl = "http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8"; private final String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() //+ "/道士下山.ts"; diff --git a/build.gradle b/build.gradle index 59427992..b6cecc15 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 377 - versionName = '3.7.7' + versionCode = 378 + versionName = '3.7.8-pre-1' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 944d69a1afa3141aac0d0b08ead193e6a3087eb1 Mon Sep 17 00:00:00 2001 From: lyy Date: Thu, 21 Nov 2019 10:37:21 +0800 Subject: [PATCH 024/140] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9625c201..999bae92 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-v_3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-v_3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-v_3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-v_3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.8-pre-1-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:v_3.7.8-pre-1' -annotationProcessor 'com.arialyy.aria:compiler:v_3.7.8-pre-1' -implementation 'com.arialyy.aria:ftpComponent:v_3.7.8-pre-1' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:v_3.7.8-pre-1' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.8-pre-1' +annotationProcessor 'com.arialyy.aria:compiler:3.7.8-pre-1' +implementation 'com.arialyy.aria:ftpComponent:3.7.8-pre-1' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.8-pre-1' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,7 +137,7 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_v_3.7.8-pre-1 (2019/11/20) + + v_3.7.8-pre-1 (2019/11/20) - fix bug https://github.com/AriaLyy/Aria/issues/526 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) From 06dd6b35e0a5c310dcce416248765faf9cb354e6 Mon Sep 17 00:00:00 2001 From: lyy Date: Tue, 26 Nov 2019 15:39:47 +0800 Subject: [PATCH 025/140] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 999bae92..36343c40 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ Aria有以下特点: [![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.8-pre-1-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.8-pre-1' -annotationProcessor 'com.arialyy.aria:compiler:3.7.8-pre-1' -implementation 'com.arialyy.aria:ftpComponent:3.7.8-pre-1' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.8-pre-1' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.8-pre-3' +annotationProcessor 'com.arialyy.aria:compiler:3.7.8-pre-3' +implementation 'com.arialyy.aria:ftpComponent:3.7.8-pre-3' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.8-pre-3' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` From 846235a8263f23dcccd91c23060ba3d5c2a5d823 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Tue, 26 Nov 2019 19:28:01 +0800 Subject: [PATCH 026/140] fix bug https://github.com/AriaLyy/Aria/issues/533 fix bug https://github.com/AriaLyy/Aria/issues/535 --- .../aria/core/command/ResumeAllCmd.java | 25 ++----------------- .../aria/core/command/ResumeThread.java | 22 ++++++++-------- .../arialyy/aria/core/command/StartCmd.java | 3 ++- README.md | 2 +- .../core/download/SingleTaskActivity.java | 2 +- build.gradle | 2 +- 6 files changed, 19 insertions(+), 37 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java index 667598ac..71b07041 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java @@ -16,29 +16,10 @@ package com.arialyy.aria.core.command; import com.arialyy.aria.core.AriaManager; -import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.manager.TaskWrapperManager; -import com.arialyy.aria.core.queue.DGroupTaskQueue; -import com.arialyy.aria.core.queue.DTaskQueue; -import com.arialyy.aria.core.queue.UTaskQueue; -import com.arialyy.aria.core.task.AbsTask; -import com.arialyy.aria.core.upload.UTaskWrapper; -import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.NetUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; /** * Created by AriaL on 2017/6/13. @@ -50,7 +31,6 @@ import java.util.concurrent.Executors; */ final class ResumeAllCmd extends AbsNormalCmd { - ResumeAllCmd(T entity, int taskType) { super(entity, taskType); } @@ -60,8 +40,7 @@ final class ResumeAllCmd extends AbsNormalCmd { ALog.w(TAG, "恢复任务失败,网络未连接"); return; } - new Thread(new ResumeThread(isDownloadCmd, IEntity.STATE_COMPLETE)).start(); + new Thread(new ResumeThread(isDownloadCmd, + String.format("state!=%s", IEntity.STATE_COMPLETE))).start(); } - - } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java index 64efead9..32f442d6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeThread.java @@ -47,11 +47,11 @@ public class ResumeThread implements Runnable { private String TAG = CommonUtil.getClassName(getClass()); private List mWaitList = new ArrayList<>(); private boolean isDownloadCmd; - private int excludeState; + private String sqlCondition; - ResumeThread(boolean isDownload, int excludeState) { + ResumeThread(boolean isDownload, String sqlCondition) { this.isDownloadCmd = isDownload; - this.excludeState = excludeState; + this.sqlCondition = sqlCondition; } /** @@ -63,8 +63,8 @@ public class ResumeThread implements Runnable { if (type == 1) { List entities = DbEntity.findDatas(DownloadEntity.class, - "isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", - String.valueOf(excludeState)); + String.format("NOT(isGroupChild) AND NOT(isComplete) AND %s ORDER BY stopTime DESC", + sqlCondition)); if (entities != null && !entities.isEmpty()) { for (DownloadEntity entity : entities) { addResumeEntity(TaskWrapperManager.getInstance() @@ -73,8 +73,9 @@ public class ResumeThread implements Runnable { } } else if (type == 2) { List entities = - DbEntity.findDatas(DownloadGroupEntity.class, "state!=? ORDER BY stopTime DESC", - String.valueOf(excludeState)); + DbEntity.findDatas(DownloadGroupEntity.class, + String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC", + sqlCondition)); if (entities != null && !entities.isEmpty()) { for (DownloadGroupEntity entity : entities) { addResumeEntity( @@ -84,8 +85,9 @@ public class ResumeThread implements Runnable { } } else if (type == 3) { List entities = - DbEntity.findDatas(UploadEntity.class, "state!=? ORDER BY stopTime DESC", - String.valueOf(excludeState)); + DbEntity.findDatas(UploadEntity.class, + String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC", + sqlCondition)); if (entities != null && !entities.isEmpty()) { for (UploadEntity entity : entities) { addResumeEntity(TaskWrapperManager.getInstance() @@ -125,7 +127,7 @@ public class ResumeThread implements Runnable { queue = DGroupTaskQueue.getInstance(); } - if (queue == null){ + if (queue == null) { ALog.e(TAG, "任务类型错误"); continue; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java index 38b1367a..11983e11 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java @@ -91,6 +91,7 @@ final class StartCmd extends AbsNormalCmd { * 当缓冲队列为null时,查找数据库中所有等待中的任务 */ private void findAllWaitTask() { - new Thread(new ResumeThread(isDownloadCmd, IEntity.STATE_WAIT)).start(); + new Thread( + new ResumeThread(isDownloadCmd, String.format("state=%s", IEntity.STATE_WAIT))).start(); } } \ No newline at end of file diff --git a/README.md b/README.md index 9625c201..9f0fde97 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_v_3.7.8-pre-1 (2019/11/20) + + v_3.7.8-pre-1 (2019/11/20) - fix bug https://github.com/AriaLyy/Aria/issues/526 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 48a9f3cb..e0a02e24 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -277,7 +277,7 @@ public class SingleTaskActivity extends BaseActivity { Aria.download(this).load(mTaskId).stop(); } else { Aria.download(this).load(mTaskId) - //.updateUrl("http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk") + .updateUrl("http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk") .resume(); } break; diff --git a/build.gradle b/build.gradle index b6cecc15..80a7004e 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ task clean(type: Delete) { ext { versionCode = 378 - versionName = '3.7.8-pre-1' + versionName = '3.7.8-pre-3' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From c4cc804c67cfe37108b1eef32d37c2329eb79939 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Wed, 27 Nov 2019 21:38:16 +0800 Subject: [PATCH 027/140] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AB=8B=E5=8D=B3?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E4=BB=BB=E5=8A=A1=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E6=AD=A3=E5=B8=B8=E6=9D=A5=E8=AF=B4=EF=BC=8C=E5=BD=93?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E9=98=9F=E5=88=97=E6=BB=A1=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=81=A2=E5=A4=8D=E4=BB=BB=E5=8A=A1=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E5=8F=AA=E8=83=BD=E5=B0=86=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=94=BE=E5=88=B0=E7=BC=93=E5=AD=98=E9=98=9F=E5=88=97=E4=B8=AD?= =?UTF-8?q?=E3=80=82=E5=A6=82=E6=9E=9C=E5=B8=8C=E6=9C=9B=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E6=8E=A5=E5=8F=A3=EF=BC=8C=E9=A9=AC=E4=B8=8A?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=E6=89=A7=E8=A1=8C=E9=98=9F=E5=88=97=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E8=B0=83=E7=94=A8`resume(true)`=E8=BF=99?= =?UTF-8?q?=E4=B8=AA=E9=87=8D=E8=BD=BD=E6=96=B9=E6=B3=95=E3=80=82=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA=E9=9D=9E=E5=88=86=E5=9D=97?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=EF=BC=8C=E6=97=A0=E6=B3=95=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E4=B8=8B=E8=BD=BD=E7=9A=84=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0M3U8=E5=8A=A0=E5=AF=86=E5=AF=86=E9=92=A5?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E8=BD=BD=E5=9C=B0=E5=9D=80=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E5=99=A8=20https://github.com/AriaLyy/Aria/issues/522?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/command/AbsCmdFactory.java | 4 +-- .../aria/core/command/AbsNormalCmd.java | 8 ----- .../aria/core/command/NormalCmdFactory.java | 4 +-- .../arialyy/aria/core/command/StartCmd.java | 18 +++++++++-- .../aria/core/common/AbsNormalTarget.java | 12 ++++++- .../common/controller/INormalFeature.java | 8 +++++ .../common/controller/NormalController.java | 20 ++++++++++-- .../aria/core/download/m3u8/M3U8Option.java | 13 ++++++++ DEV_LOG.md | 7 +++- .../http/download/HttpDThreadTaskAdapter.java | 1 + .../com/arialyy/aria/m3u8/M3U8InfoThread.java | 12 +++++-- .../com/arialyy/aria/m3u8/M3U8TaskOption.java | 14 ++++++++ .../aria/core/common/RecordHelper.java | 3 +- .../aria/core/processor/IKeyUrlConverter.java | 32 +++++++++++++++++++ .../com/arialyy/aria/util/CommonUtil.java | 16 +++++----- app/src/main/assets/aria_config.xml | 2 +- .../core/download/HttpDownloadModule.java | 4 +-- .../core/download/SingleTaskActivity.java | 3 +- .../core/download/mutil/DownloadAdapter.java | 8 ++--- 19 files changed, 151 insertions(+), 38 deletions(-) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/processor/IKeyUrlConverter.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java index 2af0e2ff..1a7cbf34 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/AbsCmdFactory.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.command; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -25,8 +26,7 @@ public abstract class AbsCmdFactory extends AbsCmd { mQueue.resumeTask(task); } - /** - * 启动指定任务 - * - * @param task 指定任务 - */ - void startTask(AbsTask task) { - mQueue.startTask(task); - } /** * 从队列中获取任务 diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java b/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java index 8ba64474..194b7c32 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/NormalCmdFactory.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.command; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -83,8 +84,7 @@ public class NormalCmdFactory extends AbsCmdFactory extends AbsNormalCmd { +final public class StartCmd extends AbsNormalCmd { + + private boolean newStart = false; StartCmd(T entity, int taskType) { super(entity, taskType); } + /** + * 立即执行任务 + * @param newStart true 立即执行任务,无论执行队列是否满了 + */ + public void setNewStart(boolean newStart) { + this.newStart = newStart; + } + @Override public void executeCmd() { if (!canExeCmd) return; if (!NetUtils.isConnected(AriaManager.getInstance().getAPP())) { @@ -71,7 +81,11 @@ final class StartCmd extends AbsNormalCmd { startTask(); } } else { - sendWaitState(task); + if (newStart){ + startTask(); + }else { + sendWaitState(task); + } } } } else { diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index 110c18f0..b8572a54 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -150,7 +150,17 @@ public abstract class AbsNormalTarget extends Ab */ @Override public void resume() { - getController().resume(); + resume(false); + } + + /** + * 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。 + * 如果希望调用恢复接口,马上进入执行队列,需要使用该方法 + * + * @param newStart true 立即将任务恢复到执行队列中 + */ + @Override public void resume(boolean newStart) { + getController().resume(newStart); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java index 550e5a50..968098fd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java @@ -30,6 +30,14 @@ public interface INormalFeature { */ void resume(); + /** + * 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。 + * 如果希望调用恢复接口,马上进入执行队列,需要使用该方法 + * + * @param newStart true 立即将任务恢复到执行队列中 + */ + void resume(boolean newStart); + /** * 删除任务 */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java index 9567d908..8d019d01 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java @@ -16,11 +16,12 @@ package com.arialyy.aria.core.common.controller; import com.arialyy.aria.core.command.CancelCmd; +import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.core.command.NormalCmdFactory; +import com.arialyy.aria.core.command.StartCmd; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.core.command.CmdHelper; /** * 启动控制器 @@ -49,10 +50,23 @@ public final class NormalController extends FeatureController implements INormal */ @Override public void resume() { + resume(false); + } + + /** + * 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。 + * 如果希望调用恢复接口,马上进入执行队列,需要使用该方法 + * + * @param newStart true 立即将任务恢复到执行队列中 + */ + @Override public void resume(boolean newStart) { if (checkConfig()) { + StartCmd cmd = + (StartCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, + checkTaskType()); + cmd.setNewStart(newStart); EventMsgUtil.getDefault() - .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, - checkTaskType())); + .post(cmd); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java index d9b56734..eb921c8a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.download.m3u8; import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.IKeyUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.ComponentUtil; @@ -31,6 +32,7 @@ public class M3U8Option extends BaseOption { private int bandWidth; private ITsMergeHandler mergeHandler; private IBandWidthUrlConverter bandWidthUrlConverter; + private IKeyUrlConverter keyUrlConverter; M3U8Option() { super(); @@ -88,4 +90,15 @@ public class M3U8Option extends BaseOption { this.bandWidthUrlConverter = bandWidthUrlConverter; return (OP) this; } + + /** + * M3U8 密钥url转换器,对于某些服务器,密钥的下载地址是被加密的,因此需要使用该方法将被加密的密钥解密成可被识别的http地址 + * + * @param keyUrlConverter {@link IKeyUrlConverter} + */ + public OP setKeyUrlConverter(IKeyUrlConverter keyUrlConverter) { + CheckUtil.checkMemberClass(keyUrlConverter.getClass()); + this.keyUrlConverter = keyUrlConverter; + return (OP) this; + } } diff --git a/DEV_LOG.md b/DEV_LOG.md index 6d99e34d..550522d4 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,6 +1,11 @@ ## 开发日志 - + v_3.7.8-pre-1(2019/11/20) + + v_3.7.8 - fix bug https://github.com/AriaLyy/Aria/issues/526 + - fix bug https://github.com/AriaLyy/Aria/issues/533 + - fix bug https://github.com/AriaLyy/Aria/issues/535 + - 修复ftp无法完成下载的问题 + - 增加立即恢复任务的接口,正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。如果希望调用恢复接口,马上进入执行队列,需要调用`resume(true)`这个重载方法。 + - 增加M3U8加密密钥的下载地址转换器 https://github.com/AriaLyy/Aria/issues/522 + v_3.7.7 (2019/11/20) - 修复ftp无法完成下载的问题 - 修复一个http下载崩溃的问题 diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index b9cfe3ea..3f5ca877 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -24,6 +24,7 @@ import com.arialyy.aria.http.BaseHttpThreadTaskAdapter; import com.arialyy.aria.http.ConnectionHelp; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.FileUtil; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java index 9725c860..d92589b8 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java @@ -26,6 +26,7 @@ import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.IKeyUrlConverter; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.M3U8Exception; @@ -127,7 +128,7 @@ final public class M3U8InfoThread implements Runnable { File indexFile = new File(indexPath); if (!indexFile.exists()) { FileUtil.createFile(indexPath); - }else { + } else { //FileUtil.deleteFile(indexPath); } fos = new FileOutputStream(indexFile); @@ -353,7 +354,14 @@ final public class M3U8InfoThread implements Runnable { } else { return; } - URL url = ConnectionHelp.handleUrl(info.keyUrl, mHttpOption); + + IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter(); + String keyUrl = info.keyUrl; + if (keyUrlConverter != null) { + keyUrl = keyUrlConverter.convert(keyUrl); + } + + URL url = ConnectionHelp.handleUrl(keyUrl, mHttpOption); conn = ConnectionHelp.handleConnection(url, mHttpOption); ConnectionHelp.setConnectParam(mHttpOption, conn); conn.setConnectTimeout(mConnectTimeOut); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index cd1c663d..5e9df9fe 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -17,6 +17,7 @@ package com.arialyy.aria.m3u8; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.processor.IBandWidthUrlConverter; +import com.arialyy.aria.core.processor.IKeyUrlConverter; import com.arialyy.aria.core.processor.ILiveTsUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.processor.IVodTsUrlConverter; @@ -103,6 +104,19 @@ public class M3U8TaskOption implements ITaskOption { */ private boolean generateIndexFile = false; + /** + * 加密密钥的解密处理器 + */ + private SoftReference keyUrlConverter; + + public IKeyUrlConverter getKeyUrlConverter() { + return keyUrlConverter == null ? null : keyUrlConverter.get(); + } + + public void setKeyUrlConverter(IKeyUrlConverter keyUrlConverter) { + this.keyUrlConverter = new SoftReference<>(keyUrlConverter); + } + public boolean isGenerateIndexFile() { return generateIndexFile; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index f6fc6f97..12c2dd3d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -63,7 +63,8 @@ public class RecordHelper { fileExists = true; } // 处理文件被删除的情况 - if (fileExists) { + if (!fileExists) { + ALog.w(TAG, String.format("文件【%s】被删除,重新分配线程区间", mTaskRecord.filePath)); for (int i = 0; i < mTaskRecord.threadNum; i++) { long startL = i * blockSize, endL = (i + 1) * blockSize; ThreadRecord tr = mTaskRecord.threadRecords.get(i); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IKeyUrlConverter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IKeyUrlConverter.java new file mode 100644 index 00000000..fd544746 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/IKeyUrlConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.processor; + +import com.arialyy.aria.core.inf.IEventHandler; + +/** + * M3U8 密钥下载地址处理器,可用于解密被加密的密钥的下载地址 + */ +public interface IKeyUrlConverter extends IEventHandler { + + /** + * 将被加密的密钥下载地址转换为可使用的http下载地址 + * + * @param keyUrl 加密的url地址 + * @return 可正常访问的http地址 + */ + String convert(String keyUrl); +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index 07eb8088..c046381f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -71,15 +71,15 @@ public class CommonUtil { ALog.e(TAG, "sql语句表达式不能为null"); return false; } - if (expression.length == 1) { - ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression))); - return false; - } + //if (expression.length == 1) { + // ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression))); + // return false; + //} String where = expression[0]; - if (!where.contains("?")) { - ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression))); - return false; - } + //if (!where.contains("?")) { + // ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression))); + // return false; + //} Pattern pattern = Pattern.compile("\\?"); Matcher matcher = pattern.matcher(where); int count = 0; diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index cb8d1c24..e4ea2d42 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -32,7 +32,7 @@ 3、只对新的多线程下载任务有效 4、只对多线程的任务有效 --> - + - + -## 预期行为 - - -## 当前行为 +## 版本 +* 框架版本 +* 系统版本 -## 可能的解决方案 - +## 错误日志 + ## 重现步骤 - 1. 2. 3. 4. -## 错误日志 - - -## 版本 -* 框架版本 - -* 系统版本 From f5f8e9b528cde21ccbed8dc8b284f8e37d53e226 Mon Sep 17 00:00:00 2001 From: lyy Date: Thu, 28 Nov 2019 16:39:46 +0800 Subject: [PATCH 030/140] Update Custom.md --- .github/ISSUE_TEMPLATE/Custom.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index f8a8a4dc..e25f53bc 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -4,6 +4,7 @@ about: 提交问题前,请先阅读文档和搜索issue --- + ## 版本 * 框架版本 From 297cc99f2b01e696c4316c49503480b0ba21b75b Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Thu, 28 Nov 2019 19:48:50 +0800 Subject: [PATCH 031/140] fix bug https://github.com/AriaLyy/Aria/issues/537 --- .../com/arialyy/aria/core/AriaManager.java | 3 -- DEV_LOG.md | 2 ++ README.md | 28 ++++++++----------- app/build.gradle | 2 ++ .../arialyy/simple/base/BaseApplication.java | 2 +- .../download/m3u8/M3U8LiveDLoadActivity.java | 9 ++++-- build.gradle | 4 +-- 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index be47804d..8a883124 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -98,9 +98,6 @@ import java.util.concurrent.ConcurrentHashMap; } public static AriaManager getInstance() { - if (INSTANCE == null) { - throw new NullPointerException("请使用AriaManager.init(context)初始化管理器"); - } return INSTANCE; } diff --git a/DEV_LOG.md b/DEV_LOG.md index 2fa357ca..8e1e05b5 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,6 @@ ## 开发日志 + + v_3.7.9 (2019/11/28) + - fix bug https://github.com/AriaLyy/Aria/issues/537 + v_3.7.8 (2019/11/28) - fix bug https://github.com/AriaLyy/Aria/issues/526 - fix bug https://github.com/AriaLyy/Aria/issues/533 diff --git a/README.md b/README.md index 1885981c..20cbf846 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.8-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.8-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.8-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.8-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.9-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.9-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.9-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.9-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.8' -annotationProcessor 'com.arialyy.aria:compiler:3.7.8' -implementation 'com.arialyy.aria:ftpComponent:3.7.8' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.8' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.9' +annotationProcessor 'com.arialyy.aria:compiler:3.7.9' +implementation 'com.arialyy.aria:ftpComponent:3.7.9' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.9' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,15 +137,9 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.8 (2019/11/28) - - fix bug https://github.com/AriaLyy/Aria/issues/526 - - fix bug https://github.com/AriaLyy/Aria/issues/533 - - fix bug https://github.com/AriaLyy/Aria/issues/535 - - 修复ftp无法完成下载的问题 - - 修复一个非分块模式下,调用`updateUrl(newUrl)`后无法恢复下载的问题 - - 增加立即恢复任务的接口,正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。如果希望调用恢复接口,马上进入执行队列,需要调用`resume(true)`这个重载方法。 - - 增加M3U8加密密钥的下载地址转换器 https://github.com/AriaLyy/Aria/issues/522 - + + v_3.7.9 (2019/11/28) + - fix bug https://github.com/AriaLyy/Aria/issues/537 + [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) ## 混淆配置 diff --git a/app/build.gradle b/app/build.gradle index d933ece2..50859d46 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,6 +72,8 @@ dependencies { implementation project(':M3U8Component') implementation project(':FtpComponent') implementation project(path: ':AriaAnnotations') + + debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' } repositories { mavenCentral() diff --git a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java index 15d6f2fc..5d0b3e17 100644 --- a/app/src/main/java/com/arialyy/simple/base/BaseApplication.java +++ b/app/src/main/java/com/arialyy/simple/base/BaseApplication.java @@ -34,7 +34,7 @@ public class BaseApplication extends Application { super.onCreate(); INSTANCE = this; AbsFrame.init(this); - Aria.init(this); + //Aria.init(this); if (BuildConfig.DEBUG) { //StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() // .detectAll() diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java index 0d0f603f..1a9731a1 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java @@ -49,6 +49,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity private String mFilePath; private M3U8LiveModule mModule; private DownloadEntity mEntity; + private long mTaskId; @Override protected void init(Bundle savedInstanceState) { @@ -63,6 +64,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity return; } mEntity = entity; + mTaskId = mEntity.getId(); getBinding().setStateStr(getString(R.string.start)); getBinding().setUrl(entity.getUrl()); getBinding().setFilePath(entity.getFilePath()); @@ -213,22 +215,23 @@ public class M3U8LiveDLoadActivity extends BaseActivity public void onClick(View view) { switch (view.getId()) { case R.id.start: - if (Aria.download(this).load(mEntity.getId()).isRunning()) { + if (Aria.download(this).load(mTaskId).isRunning()) { Aria.download(this).load(mEntity.getId()).stop(); } else { startD(); } break; case R.id.cancel: - if (AppUtil.chekEntityValid(mEntity)) { + if (mTaskId != -1){ Aria.download(this).load(mEntity.getId()).cancel(true); + mTaskId = -1; } break; } } private void startD() { - Aria.download(M3U8LiveDLoadActivity.this) + mTaskId = Aria.download(M3U8LiveDLoadActivity.this) .load(mUrl) .setFilePath(mFilePath, true) .m3u8LiveOption(getLiveoption()) diff --git a/build.gradle b/build.gradle index 74b78d1f..752aad52 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 378 - versionName = '3.7.8' + versionCode = 379 + versionName = '3.7.9' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 669aa6981c56f47fa38e402b095818f5c6c1ce9d Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 2 Dec 2019 19:31:39 +0800 Subject: [PATCH 032/140] =?UTF-8?q?fix=20bug=20https://github.com/AriaLyy/?= =?UTF-8?q?Aria/issues/543#issuecomment-559733124=20fix=20bug=20https://gi?= =?UTF-8?q?thub.com/AriaLyy/Aria/issues/542=20fix=20bug=20https://github.c?= =?UTF-8?q?om/AriaLyy/Aria/issues/547=20=E4=BF=AE=E5=A4=8D=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=A4=B1=E8=B4=A5=E6=97=B6=EF=BC=8C=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E6=97=A0=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E5=BF=BD=E7=95=A5=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E7=9A=84api=EF=BC=8C`ignoreCheckPermissions(?= =?UTF-8?q?)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/arialyy/aria/core/AriaManager.java | 10 -- .../aria/core/command/HighestPriorityCmd.java | 3 +- .../aria/core/command/ResumeAllCmd.java | 4 +- .../arialyy/aria/core/command/StartCmd.java | 15 +-- .../aria/core/common/AbsBuilderTarget.java | 10 +- .../aria/core/common/AbsNormalTarget.java | 8 ++ .../common/controller/FeatureController.java | 29 ++++-- .../aria/core/download/CheckDEntityUtil.java | 4 +- .../aria/core/download/DownloadReceiver.java | 5 +- .../aria/core/queue/DGroupTaskQueue.java | 8 +- .../arialyy/aria/core/queue/DTaskQueue.java | 10 +- .../arialyy/aria/core/queue/UTaskQueue.java | 5 +- .../core/queue/pool/DGLoadExecutePool.java | 3 +- .../core/queue/pool/DLoadExecutePool.java | 4 +- .../core/queue/pool/UploadExecutePool.java | 4 +- .../core/scheduler/FailureTaskHandler.java | 67 +++++++------ .../aria/core/scheduler/TaskSchedulers.java | 28 +++--- .../aria/core/upload/UploadReceiver.java | 3 +- DEV_LOG.md | 6 ++ .../ftp/upload/FtpUThreadTaskAdapter.java | 10 +- .../com/arialyy/aria/m3u8/BaseM3U8Loader.java | 4 + .../arialyy/aria/m3u8/M3U8RecordAdapter.java | 3 +- .../com/arialyy/aria/core/AriaConfig.java | 10 ++ .../aria/core/listener/UploadListener.java | 55 ----------- .../aria/core/loader/ThreadStateManager.java | 2 +- .../arialyy/aria/core/task/ThreadTask.java | 17 ++-- .../java/com/arialyy/aria/orm/DBConfig.java | 4 +- .../com/arialyy/aria/orm/DelegateCommon.java | 18 ++++ .../java/com/arialyy/aria/orm/SqlHelper.java | 91 ++++++++++-------- .../java/com/arialyy/aria/util/CheckUtil.java | 2 +- .../java/com/arialyy/aria/util/FileUtil.java | 45 ++++++++- .../core/download/HttpDownloadModule.java | 11 ++- .../core/download/SingleTaskActivity.java | 8 +- .../simple/core/upload/UploadModule.java | 4 +- build.gradle | 4 +- py/.vscode/launch.json | 15 --- py/.vscode/settings.json | 6 -- test/347/AndroidAria.db | Bin 0 -> 73728 bytes 38 files changed, 294 insertions(+), 241 deletions(-) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java delete mode 100644 py/.vscode/launch.json delete mode 100644 py/.vscode/settings.json create mode 100644 test/347/AndroidAria.db diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index 8a883124..0d0c2f4a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -23,8 +23,6 @@ import android.app.Dialog; import android.content.Context; import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.widget.PopupWindow; import com.arialyy.aria.core.command.CommandManager; import com.arialyy.aria.core.common.QueueMod; @@ -76,7 +74,6 @@ import java.util.concurrent.ConcurrentHashMap; */ private Map> mSubClass = new ConcurrentHashMap<>(); private static Context APP; - private Handler mAriaHandler; private DelegateWrapper mDbWrapper; private AriaConfig mConfig; @@ -168,13 +165,6 @@ import java.util.concurrent.ConcurrentHashMap; } } - public synchronized Handler getAriaHandler() { - if (mAriaHandler == null) { - mAriaHandler = new Handler(Looper.getMainLooper()); - } - return mAriaHandler; - } - public Map getReceiver() { return mReceivers; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java index 692d083c..d2d8b228 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/HighestPriorityCmd.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.command; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -41,7 +42,7 @@ final class HighestPriorityCmd extends AbsNormalCmd @Override public void executeCmd() { if (!canExeCmd) return; - if (!NetUtils.isConnected(AriaManager.getInstance().getAPP())){ + if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP())){ ALog.e(TAG, "启动任务失败,网络未连接"); return; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java index 71b07041..4e81b18a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ResumeAllCmd.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; @@ -36,7 +36,7 @@ final class ResumeAllCmd extends AbsNormalCmd { } @Override public void executeCmd() { - if (!NetUtils.isConnected(AriaManager.getInstance().getAPP())) { + if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP())) { ALog.w(TAG, "恢复任务失败,网络未连接"); return; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java index 4e0e3661..f6b7cc2c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/StartCmd.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.command; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.common.QueueMod; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.AbsTask; @@ -37,6 +37,7 @@ final public class StartCmd extends AbsNormalCmd { /** * 立即执行任务 + * * @param newStart true 立即执行任务,无论执行队列是否满了 */ public void setNewStart(boolean newStart) { @@ -45,17 +46,17 @@ final public class StartCmd extends AbsNormalCmd { @Override public void executeCmd() { if (!canExeCmd) return; - if (!NetUtils.isConnected(AriaManager.getInstance().getAPP())) { + if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP())) { ALog.e(TAG, "启动任务失败,网络未连接"); return; } String mod; int maxTaskNum = mQueue.getMaxTaskNum(); - AriaManager manager = AriaManager.getInstance(); + AriaConfig config = AriaConfig.getInstance(); if (isDownloadCmd) { - mod = manager.getDownloadConfig().getQueueMod(); + mod = config.getDConfig().getQueueMod(); } else { - mod = manager.getUploadConfig().getQueueMod(); + mod = config.getUConfig().getQueueMod(); } AbsTask task = getTask(); @@ -81,9 +82,9 @@ final public class StartCmd extends AbsNormalCmd { startTask(); } } else { - if (newStart){ + if (newStart) { startTask(); - }else { + } else { sendWaitState(task); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java index c130894b..4ce228e3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java @@ -15,8 +15,8 @@ */ package com.arialyy.aria.core.common; -import com.arialyy.aria.core.common.controller.IStartFeature; import com.arialyy.aria.core.common.controller.BuilderController; +import com.arialyy.aria.core.common.controller.IStartFeature; import com.arialyy.aria.core.inf.AbsTarget; /** @@ -34,6 +34,14 @@ public abstract class AbsBuilderTarget extends return mStartController; } + /** + * 是否忽略权限检查 + */ + public TARGET ignoreCheckPermissions() { + getController().ignoreCheckPermissions(); + return (TARGET) this; + } + /** * 添加任务 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index b8572a54..71a6c789 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -31,6 +31,14 @@ import com.arialyy.aria.util.RecordUtil; public abstract class AbsNormalTarget extends AbsTarget implements INormalFeature { + /** + * 是否忽略权限检查 + */ + public TARGET ignoreCheckPermissions() { + getController().ignoreCheckPermissions(); + return (TARGET) this; + } + /** * 任务是否在执行 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java index d3bbca51..1ceab180 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java @@ -19,21 +19,21 @@ import android.Manifest; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.download.CheckDEntityUtil; import com.arialyy.aria.core.download.CheckDGEntityUtil; import com.arialyy.aria.core.download.CheckFtpDirEntityUtil; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.ICheckEntityUtil; -import com.arialyy.aria.core.task.ITask; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.scheduler.TaskSchedulers; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.upload.CheckUEntityUtil; import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Constructor; @@ -46,6 +46,10 @@ public abstract class FeatureController { private final String TAG; private AbsTaskWrapper mTaskWrapper; + /** + * 是否忽略权限检查 true 忽略权限检查 + */ + private boolean ignoreCheckPermissions = false; FeatureController(AbsTaskWrapper wrapper) { mTaskWrapper = wrapper; @@ -87,6 +91,13 @@ public abstract class FeatureController { return null; } + /** + * 是否忽略权限检查 + */ + public void ignoreCheckPermissions() { + this.ignoreCheckPermissions = true; + } + protected AbsTaskWrapper getTaskWrapper() { return mTaskWrapper; } @@ -111,7 +122,7 @@ public abstract class FeatureController { * 如果检查实体失败,将错误回调 */ boolean checkConfig() { - if (!checkPermission()) { + if (!ignoreCheckPermissions && !checkPermission()) { return false; } boolean b = checkEntity(); @@ -134,21 +145,21 @@ public abstract class FeatureController { */ private boolean checkPermission() { - if (AriaManager.getInstance() + if (AriaConfig.getInstance() .getAPP() .checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ALog.e(TAG, "启动失败,缺少权限:Manifest.permission.WRITE_EXTERNAL_STORAGE"); return false; } - if (AriaManager.getInstance() + if (AriaConfig.getInstance() .getAPP() .checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) { ALog.e(TAG, "启动失败,缺少权限:Manifest.permission.INTERNET"); return false; } - if (AriaManager.getInstance() + if (AriaConfig.getInstance() .getAPP() .checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 3f7e149c..c6eb5dcf 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -23,6 +23,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.RecordUtil; import java.io.File; @@ -65,8 +66,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { File file = new File(mWrapper.getTempFilePath()); Object bw = mWrapper.getM3U8Params().getParam(IOptionConstant.bandWidth); int bandWidth = bw == null ? 0 : (int) bw; - // 缓存文件夹格式:问文件夹/.文件名_码率 - String cacheDir = String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); + String cacheDir = FileUtil.getTsCacheDir(file.getPath(), bandWidth); mWrapper.getM3U8Params().setParams(IOptionConstant.cacheDir, cacheDir); M3U8Entity m3U8Entity = mEntity.getM3U8Entity(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 4f91b0db..cbb6c5e8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.CancelAllCmd; import com.arialyy.aria.core.command.CmdHelper; @@ -64,7 +65,7 @@ public class DownloadReceiver extends AbsReceiver { */ @Deprecated public DownloadReceiver setMaxSpeed(int maxSpeed) { - AriaManager.getInstance().getDownloadConfig().setMaxSpeed(maxSpeed); + AriaConfig.getInstance().getDConfig().setMaxSpeed(maxSpeed); return this; } @@ -287,7 +288,7 @@ public class DownloadReceiver extends AbsReceiver { * @return 如果实体不存在,返回null */ public DownloadGroupEntity getGroupEntity(List urls) { - if (CheckUtil.checkDownloadUrlsIsEmpty(urls)){ + if (CheckUtil.checkDownloadUrlsIsEmpty(urls)) { return null; } return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls)); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java index ce59202b..fe2829d7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DGroupTaskQueue.java @@ -16,13 +16,13 @@ package com.arialyy.aria.core.queue; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.core.event.DGMaxNumEvent; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.scheduler.TaskSchedulers; +import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.util.ALog; /** @@ -57,7 +57,7 @@ public class DGroupTaskQueue } @Override public int getMaxTaskNum() { - return AriaManager.getInstance().getDGroupConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getDConfig().getMaxTaskNum(); } @Override public DownloadGroupTask createTask(DGTaskWrapper wrapper) { @@ -74,6 +74,6 @@ public class DGroupTaskQueue } @Override public int getOldMaxNum() { - return AriaManager.getInstance().getDGroupConfig().oldMaxTaskNum; + return AriaConfig.getInstance().getDGConfig().oldMaxTaskNum; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java index eb552fad..ae0ee5bc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java @@ -16,14 +16,14 @@ package com.arialyy.aria.core.queue; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.event.DMaxNumEvent; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.scheduler.TaskSchedulers; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import java.util.LinkedHashSet; import java.util.Map; @@ -60,7 +60,7 @@ public class DTaskQueue extends AbsTaskQueue { } @Override public int getOldMaxNum() { - return AriaManager.getInstance().getDownloadConfig().oldMaxTaskNum; + return AriaConfig.getInstance().getDConfig().oldMaxTaskNum; } /** @@ -81,7 +81,7 @@ public class DTaskQueue extends AbsTaskQueue { } } } - int maxSize = AriaManager.getInstance().getDownloadConfig().getMaxTaskNum(); + int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum(); int currentSize = mExecutePool.size(); if (currentSize == 0 || currentSize < maxSize) { startTask(task); @@ -126,6 +126,6 @@ public class DTaskQueue extends AbsTaskQueue { } @Override public int getMaxTaskNum() { - return AriaManager.getInstance().getDownloadConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getDConfig().getMaxTaskNum(); } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java index 026096ec..cbf15f7c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/UTaskQueue.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.queue; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; @@ -55,11 +56,11 @@ public class UTaskQueue extends AbsTaskQueue { } @Override public int getOldMaxNum() { - return AriaManager.getInstance().getUploadConfig().oldMaxTaskNum; + return AriaConfig.getInstance().getUConfig().oldMaxTaskNum; } @Override public int getMaxTaskNum() { - return AriaManager.getInstance().getUploadConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getUConfig().getMaxTaskNum(); } @Override public UploadTask createTask(UTaskWrapper wrapper) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java index 5a494822..099c1002 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core.queue.pool; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.task.AbsTask; @@ -26,6 +27,6 @@ class DGLoadExecutePool extends DLoadExecutePool { private final String TAG = "DGLoadExecutePool"; @Override protected int getMaxSize() { - return AriaManager.getInstance().getDGroupConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getDGConfig().getMaxTaskNum(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java index be863d4e..d8db105f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.queue.pool; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -30,7 +30,7 @@ class DLoadExecutePool extends BaseExecutePool { private final String TAG = "DownloadExecutePool"; @Override protected int getMaxSize() { - return AriaManager.getInstance().getDownloadConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getDConfig().getMaxTaskNum(); } @Override public boolean putTask(TASK task) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java index ae31ef4b..c8af2622 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/UploadExecutePool.java @@ -15,7 +15,7 @@ */ package com.arialyy.aria.core.queue.pool; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.task.AbsTask; /** @@ -23,6 +23,6 @@ import com.arialyy.aria.core.task.AbsTask; */ public class UploadExecutePool extends BaseExecutePool { @Override protected int getMaxSize() { - return AriaManager.getInstance().getUploadConfig().getMaxTaskNum(); + return AriaConfig.getInstance().getUConfig().getMaxTaskNum(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java index 1988cba7..a578d951 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/FailureTaskHandler.java @@ -15,11 +15,11 @@ */ package com.arialyy.aria.core.scheduler; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.manager.TaskWrapperManager; import com.arialyy.aria.core.queue.ITaskQueue; +import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; @@ -42,7 +42,6 @@ class FailureTaskHandler { private TaskSchedulers mSchedulers; private final ReentrantLock LOCK = new ReentrantLock(); private Condition mCondition = LOCK.newCondition(); - private AriaManager mAriaManager; static FailureTaskHandler init(TaskSchedulers schedulers) { if (INSTANCE == null) { @@ -57,7 +56,6 @@ class FailureTaskHandler { private FailureTaskHandler(TaskSchedulers schedulers) { mSchedulers = schedulers; - mAriaManager = AriaManager.getInstance(); new Thread(new Runnable() { @Override public void run() { while (true) { @@ -82,35 +80,46 @@ class FailureTaskHandler { } private void retryTask(final TASK task) { - long interval = task.getTaskWrapper().getConfig().getReTryInterval(); - final int num = task.getTaskWrapper().getConfig().getReTryNum(); final ITaskQueue queue = mSchedulers.getQueue(task.getTaskType()); - mAriaManager.getAriaHandler().postDelayed(new Runnable() { - @Override public void run() { - AbsEntity entity = task.getTaskWrapper().getEntity(); - if (entity.getFailNum() <= num) { - ALog.d(TAG, String.format("任务【%s】开始重试", task.getTaskName())); - queue.reTryStart(task); - } else { - queue.removeTaskFormQueue(task.getKey()); - mSchedulers.startNextTask(queue, task.getSchedulerType()); - TaskWrapperManager.getInstance().removeTaskWrapper(task.getTaskWrapper()); - } - mExeQueue.remove(task); - int index = mHashList.indexOf(task.hashCode()); - if (index != -1) { - mHashList.remove(index); - } - if (LOCK.isLocked()) { - try { - LOCK.lock(); - mCondition.signalAll(); - } finally { - LOCK.unlock(); + if (task.isNeedRetry()){ + long interval = task.getTaskWrapper().getConfig().getReTryInterval(); + final int num = task.getTaskWrapper().getConfig().getReTryNum(); + AriaConfig.getInstance().getAriaHandler().postDelayed(new Runnable() { + @Override public void run() { + AbsEntity entity = task.getTaskWrapper().getEntity(); + if (entity.getFailNum() <= num) { + ALog.d(TAG, String.format("任务【%s】开始重试", task.getTaskName())); + queue.reTryStart(task); + } else { + queue.removeTaskFormQueue(task.getKey()); + mSchedulers.startNextTask(queue, task.getSchedulerType()); + TaskWrapperManager.getInstance().removeTaskWrapper(task.getTaskWrapper()); } + next(task); } + }, interval); + }else { + queue.removeTaskFormQueue(task.getKey()); + mSchedulers.startNextTask(queue, task.getSchedulerType()); + TaskWrapperManager.getInstance().removeTaskWrapper(task.getTaskWrapper()); + next(task); + } + } + + private void next(TASK task){ + mExeQueue.remove(task); + int index = mHashList.indexOf(task.hashCode()); + if (index != -1) { + mHashList.remove(index); + } + if (LOCK.isLocked()) { + try { + LOCK.lock(); + mCondition.signalAll(); + } finally { + LOCK.unlock(); } - }, interval); + } } void offer(TASK task) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java index 7ba7fad0..ad5bdb4c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/TaskSchedulers.java @@ -19,7 +19,7 @@ import android.content.Intent; import android.os.Bundle; import android.os.Message; import com.arialyy.annotations.TaskEnum; -import com.arialyy.aria.core.AriaManager; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.group.GroupSendParams; @@ -53,10 +53,10 @@ public class TaskSchedulers implements ISchedulers { private static FailureTaskHandler mFailureTaskHandler; private Map> mObservers = new ConcurrentHashMap<>(); - private AriaManager mAriaManager; + private AriaConfig mAriaConfig; private TaskSchedulers() { - mAriaManager = AriaManager.getInstance(); + mAriaConfig = AriaConfig.getInstance(); } public static TaskSchedulers getInstance() { @@ -230,11 +230,11 @@ public class TaskSchedulers implements ISchedulers { } } - boolean canSend = mAriaManager.getAppConfig().isUseBroadcast(); + boolean canSend = mAriaConfig.getAConfig().isUseBroadcast(); if (canSend) { Intent intent = new Intent(ISchedulers.ARIA_TASK_INFO_ACTION); intent.putExtras(data); - mAriaManager.getAPP().sendBroadcast(intent); + mAriaConfig.getAPP().sendBroadcast(intent); } return true; @@ -284,9 +284,9 @@ public class TaskSchedulers implements ISchedulers { } } - boolean canSend = mAriaManager.getAppConfig().isUseBroadcast(); + boolean canSend = mAriaConfig.getAConfig().isUseBroadcast(); if (canSend) { - mAriaManager.getAPP().sendBroadcast( + mAriaConfig.getAPP().sendBroadcast( createData(msg.what, ITask.DOWNLOAD_GROUP_SUB, params.entity)); } @@ -353,13 +353,13 @@ public class TaskSchedulers implements ISchedulers { startNextTask(getQueue(taskType), TaskSchedulerType.TYPE_DEFAULT); // 发送广播 - boolean canSend = mAriaManager.getAppConfig().isUseBroadcast(); + boolean canSend = mAriaConfig.getAConfig().isUseBroadcast(); if (canSend) { Intent intent = new Intent(ISchedulers.ARIA_TASK_INFO_ACTION); Bundle b = new Bundle(); b.putInt(ISchedulers.TASK_TYPE, taskType); b.putInt(ISchedulers.TASK_STATE, ISchedulers.FAIL); - mAriaManager.getAPP().sendBroadcast(intent); + mAriaConfig.getAPP().sendBroadcast(intent); } // 处理回调 @@ -470,16 +470,16 @@ public class TaskSchedulers implements ISchedulers { * 发送普通任务的广播 */ private void sendNormalBroadcast(int state, TASK task) { - boolean canSend = mAriaManager.getAppConfig().isUseBroadcast(); + boolean canSend = mAriaConfig.getAConfig().isUseBroadcast(); if (!canSend) { return; } int type = task.getTaskType(); if (type == ITask.DOWNLOAD || type == ITask.DOWNLOAD_GROUP) { - mAriaManager.getAPP().sendBroadcast( + mAriaConfig.getAPP().sendBroadcast( createData(state, type, task.getTaskWrapper().getEntity())); } else if (type == ITask.UPLOAD) { - mAriaManager.getAPP().sendBroadcast( + mAriaConfig.getAPP().sendBroadcast( createData(state, type, task.getTaskWrapper().getEntity())); } else { ALog.w(TAG, "发送广播失败,没有对应的任务"); @@ -519,9 +519,9 @@ public class TaskSchedulers implements ISchedulers { } int num = task.getTaskWrapper().getConfig().getReTryNum(); - boolean isNotNetRetry = mAriaManager.getAppConfig().isNotNetRetry(); + boolean isNotNetRetry = mAriaConfig.getAConfig().isNotNetRetry(); - if ((!NetUtils.isConnected(mAriaManager.getAPP()) && !isNotNetRetry) + if ((!NetUtils.isConnected(mAriaConfig.getAPP()) && !isNotNetRetry) || task.getTaskWrapper().getEntity().getFailNum() > num) { queue.removeTaskFormQueue(task.getKey()); startNextTask(queue, task.getSchedulerType()); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index fc119c45..e3f7467f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -19,6 +19,7 @@ import android.text.TextUtils; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.CancelAllCmd; import com.arialyy.aria.core.command.CmdHelper; @@ -58,7 +59,7 @@ public class UploadReceiver extends AbsReceiver { */ @Deprecated public UploadReceiver setMaxSpeed(int maxSpeed) { - AriaManager.getInstance().getUploadConfig().setMaxSpeed(maxSpeed); + AriaConfig.getInstance().getUConfig().setMaxSpeed(maxSpeed); return this; } diff --git a/DEV_LOG.md b/DEV_LOG.md index 8e1e05b5..be51081d 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,10 @@ ## 开发日志 + + v_3.7.10 + - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 + - fix bug https://github.com/AriaLyy/Aria/issues/542 + - fix bug https://github.com/AriaLyy/Aria/issues/547 + - 修复下载失败时,中断重试无效的问题 + - 增加忽略权限检查的api,`ignoreCheckPermissions()` + v_3.7.9 (2019/11/28) - fix bug https://github.com/AriaLyy/Aria/issues/537 + v_3.7.8 (2019/11/28) diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index 1f1a7ce0..891170ac 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -33,7 +33,6 @@ import java.io.UnsupportedEncodingException; * Created by Aria.Lao on 2017/7/28. D_FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限 */ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { - private final String TAG = "FtpThreadTask"; private String dir, remotePath; FtpUThreadTaskAdapter(SubThreadConfig config) { @@ -66,7 +65,8 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } file = - new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); + new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", + getTaskConfig().getBuffSize()); if (getThreadRecord().startLocation != 0) { //file.skipBytes((int) getThreadConfig().START_LOCATION); file.seek(getThreadRecord().startLocation); @@ -103,10 +103,8 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private void initPath() throws UnsupportedEncodingException { dir = CommonUtil.convertFtpChar(charSet, mTaskOption.getUrlEntity().remotePath); - String fileName = - TextUtils.isEmpty(mTaskOption.getNewFileName()) ? CommonUtil.convertFtpChar(charSet, - getEntity().getFileName()) - : CommonUtil.convertFtpChar(charSet, mTaskOption.getNewFileName()); + String fileName = TextUtils.isEmpty(mTaskOption.getNewFileName()) ? getEntity().getFileName() + : mTaskOption.getNewFileName(); remotePath = CommonUtil.convertFtpChar(charSet, diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java index 491b30f4..5fd2acd0 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.m3u8; +import android.text.TextUtils; import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; @@ -59,6 +60,9 @@ public abstract class BaseM3U8Loader extends AbsLoader { protected String getCacheDir() { String cacheDir = mM3U8Option.getCacheDir(); + if (TextUtils.isEmpty(cacheDir)) { + cacheDir = FileUtil.getTsCacheDir(getEntity().getFilePath(), mM3U8Option.getBandWidth()); + } if (!new File(cacheDir).exists()) { FileUtil.createDir(cacheDir); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 56d4050a..088f5f9d 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -131,7 +131,8 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { @Override public int initTaskThreadNum() { if (getWrapper().getRequestType() == ITaskWrapper.M3U8_VOD) { - return mOption.getUrls().size(); + return + mOption.getUrls() == null || mOption.getUrls().isEmpty() ? 1 : mOption.getUrls().size(); } if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { return 1; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java index cfe73566..8698ef0d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/AriaConfig.java @@ -23,6 +23,8 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Build; +import android.os.Handler; +import android.os.Looper; import com.arialyy.aria.core.config.AppConfig; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.config.DGroupConfig; @@ -56,6 +58,7 @@ public class AriaConfig { * 是否已经联网,true 已经联网 */ private static boolean isConnectedNet = true; + private Handler mAriaHandler; private AriaConfig(Context context) { APP = context.getApplicationContext(); @@ -105,6 +108,13 @@ public class AriaConfig { return mDGConfig; } + public synchronized Handler getAriaHandler() { + if (mAriaHandler == null) { + mAriaHandler = new Handler(Looper.getMainLooper()); + } + return mAriaHandler; + } + /** * 注册网络监听,只有配置了检查网络{@link AppConfig#isNetCheck()}才会注册事件 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java deleted file mode 100644 index 2cd1f0db..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/UploadListener.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.listener; - -import com.arialyy.aria.exception.BaseException; - -/** - * Created by lyy on 2017/2/23. - */ -class UploadListener implements IUploadListener { - @Override public void onPre() { - - } - - @Override public void onStart(long startLocation) { - - } - - @Override public void onResume(long resumeLocation) { - - } - - @Override public void onStop(long stopLocation) { - - } - - @Override public void onProgress(long currentLocation) { - - } - - @Override public void onCancel() { - - } - - @Override public void onComplete() { - - } - - @Override public void onFail(boolean needRetry, BaseException e) { - - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java index 1e6fc16a..c3fd33d8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java @@ -84,7 +84,7 @@ public class ThreadStateManager implements IThreadState { mFailNum++; if (isFail()) { Bundle b = msg.getData(); - mListener.onFail(b.getBoolean(KEY_RETRY, true), + mListener.onFail(b.getBoolean(KEY_RETRY, false), (BaseException) b.getSerializable(KEY_ERROR_INFO)); quitLooper(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 9a62b7f6..568e2b43 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -353,7 +353,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } else { ALog.e(TAG, String.format("任务【%s】执行失败", getFileName())); ErrorHelp.saveError(TAG, "", ALog.getExceptionString(ex)); - sendFailMsg(null); + sendFailMsg(null, needRetry); } } } @@ -365,7 +365,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { boolean isConnected = NetUtils.isConnected(AriaConfig.getInstance().getAPP()); if (!isConnected && !isNotNetRetry) { ALog.w(TAG, String.format("ts切片【%s】重试失败,网络未连接", getFileName())); - sendFailMsg(null); + sendFailMsg(null, false); return; } if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected( @@ -377,7 +377,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { FileUtil.createFile(mConfig.tempFile); ThreadTaskManager.getInstance().retryThread(this); } else { - sendFailMsg(null); + sendFailMsg(null, needRetry); } } @@ -389,7 +389,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private void retryBlockTask(boolean needRetry) { if (!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) { ALog.w(TAG, String.format("分块【%s】重试失败,网络未连接", getFileName())); - sendFailMsg(null); + sendFailMsg(null, false); return; } if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected( @@ -400,7 +400,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { handleBlockRecord(); ThreadTaskManager.getInstance().retryThread(this); } else { - sendFailMsg(null); + sendFailMsg(null, needRetry); } } @@ -448,13 +448,14 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { /** * 发送失败信息 */ - private void sendFailMsg(@Nullable BaseException e) { + private void sendFailMsg(@Nullable BaseException e,boolean needRetry) { + Bundle b = new Bundle(); + b.putBoolean(IThreadState.KEY_RETRY, needRetry); if (e != null) { - Bundle b = new Bundle(); b.putSerializable(IThreadState.KEY_ERROR_INFO, e); updateState(IThreadState.STATE_FAIL, b); } else { - updateState(IThreadState.STATE_FAIL, null); + updateState(IThreadState.STATE_FAIL, b); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java index 43d1e03c..7ab0b3bc 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -32,7 +32,7 @@ import java.util.Map; class DBConfig { /*adb pull /mnt/sdcard/Android/data/com.arialyy.simple/files/DB/AriaLyyDb d:/db*/ static boolean DEBUG = false; - static Map mapping = new LinkedHashMap<>(); + static Map> mapping = new LinkedHashMap<>(); static String DB_NAME; static int VERSION = 56; @@ -51,8 +51,8 @@ class DBConfig { } static { - mapping.put("DownloadGroupEntity", DownloadGroupEntity.class); mapping.put("DownloadEntity", DownloadEntity.class); + mapping.put("DownloadGroupEntity", DownloadGroupEntity.class); mapping.put("UploadEntity", UploadEntity.class); mapping.put("ThreadRecord", ThreadRecord.class); mapping.put("TaskRecord", TaskRecord.class); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java index 8021eb22..27a01d3f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -221,4 +221,22 @@ class DelegateCommon extends AbsDelegate { db.execSQL(str); } } + + /** + * 通过class 获取该class的表字段 + * + * @return 表字段列表 + */ + List getColumns(Class clazz) { + List columns = new ArrayList<>(); + List fields = CommonUtil.getAllFields(clazz); + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + columns.add(field.getName()); + } + return columns; + } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 16ae1bcb..0344b32b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -100,14 +100,14 @@ final class SqlHelper extends SQLiteOpenHelper { } else if (oldVersion < 53) { handle366Update(db); } else { - handleDbUpdate(db, null, null); + handleDbUpdate(db, null); } } } @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion > newVersion) { - handleDbUpdate(db, null, null); + handleDbUpdate(db, null); } } @@ -145,10 +145,8 @@ final class SqlHelper extends SQLiteOpenHelper { * * @param modifyColumns 需要修改的表字段的映射,key为表名, * value{@code Map}中的Map的key为老字段名称,value为该老字段对应的新字段名称 - * @param delColumns 需要删除的表字段,key为表名,value{@code List}为需要删除的字段列表 */ - private void handleDbUpdate(SQLiteDatabase db, Map> modifyColumns, - Map> delColumns) { + private void handleDbUpdate(SQLiteDatabase db, Map> modifyColumns) { if (db == null) { ALog.e("SqlHelper", "db 为 null"); return; @@ -161,7 +159,7 @@ final class SqlHelper extends SQLiteOpenHelper { db.beginTransaction(); Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { - Class clazz = DBConfig.mapping.get(tableName); + Class clazz = DBConfig.mapping.get(tableName); if (mDelegate.tableExists(db, clazz)) { //修改表名为中介表名 String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); @@ -178,46 +176,57 @@ final class SqlHelper extends SQLiteOpenHelper { // 复制数据 if (count > 0) { - // 获取旧表的所有字段名称 Cursor columnC = db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); - StringBuilder params = new StringBuilder(); + + // 获取新表的所有字段名称 + List newTabColumns = mDelegate.getColumns(clazz); + // 获取旧表的所有字段名称 + List oldTabColumns = new ArrayList<>(); while (columnC.moveToNext()) { String columnName = columnC.getString(columnC.getColumnIndex("name")); - if (delColumns != null && delColumns.get(tableName) != null) { - List delColumn = delColumns.get(tableName); - if (delColumn != null && !delColumn.isEmpty()) { - if (delColumn.contains(columnName)) { - continue; - } - } - } - - params.append(columnName).append(","); + oldTabColumns.add(columnName); } columnC.close(); + // 旧表需要删除的字段,删除旧表有而新表没的字段 + List diffTab = getDiffColumn(newTabColumns, oldTabColumns); + StringBuilder params = new StringBuilder(); + + // 需要修改的列名映射表 + Map modifyMap = null; + if (modifyColumns != null) { + modifyMap = modifyColumns.get(tableName); + } + + for (String column : oldTabColumns) { + if (!diffTab.isEmpty() && diffTab.contains(column) + && (modifyMap != null && !modifyMap.containsKey(column))) { // 如果旧表字段有修改,忽略这个删除 + continue; + } + params.append(column).append(","); + } + String oldParamStr = params.toString(); oldParamStr = oldParamStr.substring(0, oldParamStr.length() - 1); String newParamStr = oldParamStr; // 处理字段名称改变 - if (modifyColumns != null) { - Map columnMap = modifyColumns.get(tableName); - if (columnMap != null && !columnMap.isEmpty()) { - Set keys = columnMap.keySet(); - for (String key : keys) { - if (newParamStr.contains(key)) { - newParamStr = newParamStr.replace(key, columnMap.get(key)); - } + if (modifyMap != null && !modifyMap.isEmpty()) { + Set keys = modifyMap.keySet(); + for (String key : keys) { + if (newParamStr.contains(key)) { + newParamStr = newParamStr.replace(key, modifyMap.get(key)); } } } + //恢复数据 String insertSql = String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr, oldParamStr, tableName); ALog.d(TAG, "insertSql = " + insertSql); + db.execSQL(insertSql); } //删除中介表 @@ -234,6 +243,19 @@ final class SqlHelper extends SQLiteOpenHelper { } } + /** + * 取新表差值(需要删除的字段):旧表有而新表没的字段 + * + * @param newTab 新表字段 + * @param oldTab 旧表字段 + * @return 旧表有而新表没的字段 + */ + private List getDiffColumn(List newTab, List oldTab) { + List temp = new ArrayList<>(oldTab); // 拷贝旧表字段 + temp.removeAll(newTab); + return temp; + } + /** * 删除重复的repeat数据 */ @@ -256,7 +278,7 @@ final class SqlHelper extends SQLiteOpenHelper { modifyMap.put("ThreadRecord", threadRecordModify); // 执行升级操作 - handleDbUpdate(db, modifyMap, null); + handleDbUpdate(db, modifyMap); delRepeatThreadRecord(db); } @@ -273,7 +295,7 @@ final class SqlHelper extends SQLiteOpenHelper { modifyMap.put("ThreadRecord", threadRecordModify); // 执行升级操作 - handleDbUpdate(db, modifyMap, null); + handleDbUpdate(db, modifyMap); delRepeatThreadRecord(db); } @@ -307,7 +329,7 @@ final class SqlHelper extends SQLiteOpenHelper { modifyMap.put("ThreadRecord", threadRecordModify); // 执行升级操作 - handleDbUpdate(db, modifyMap, null); + handleDbUpdate(db, modifyMap); delRepeatThreadRecord(db); } @@ -360,15 +382,6 @@ final class SqlHelper extends SQLiteOpenHelper { dGEntityModifyMap.put("groupName", "groupHash"); modifyMap.put("DownloadGroupEntity", dGEntityModifyMap); - // 删除字段 - Map> delColumnMap = new HashMap<>(); - List dEntityDel = new ArrayList<>(); - dEntityDel.add("taskKey"); - delColumnMap.put("DownloadEntity", dEntityDel); - List dgEntityDel = new ArrayList<>(); - dgEntityDel.add("subtask"); - delColumnMap.put("DownloadGroupEntity", dgEntityDel); - - handleDbUpdate(db, modifyMap, delColumnMap); + handleDbUpdate(db, modifyMap); } } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index c8874bc1..7ba5a234 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -62,7 +62,7 @@ public class CheckUtil { public static boolean checkUploadPathConflicts(boolean isForceUpload, String filePath) { if (DbEntity.checkDataExist(UploadEntity.class, "filePath=?", filePath)) { if (!isForceUpload) { - ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); + ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用,请设置其它文件路径", filePath)); return false; } else { ALog.w(TAG, String.format("文件路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath)); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java index 4b1593ba..f954c7f9 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java @@ -38,6 +38,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.SequenceInputStream; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.FileChannel; @@ -61,6 +63,21 @@ public class FileUtil { private static final String EXTERNAL_STORAGE_PATH = Environment.getExternalStorageDirectory().getPath(); + /** + * 获取m3u8 ts文件的缓存目录。 + * 缓存文件夹格式:父文件夹/.文件名_码率 + * + * @param path m3u8 文件下载路径 + * @return m3u8 ts文件的缓存目录 + */ + public static String getTsCacheDir(String path, int bandWidth) { + if (TextUtils.isEmpty(path)) { + throw new NullPointerException("m3u8文保存路径为空"); + } + File file = new File(path); + return String.format("%s/.%s_%s", file.getParent(), file.getName(), bandWidth); + } + /** * 创建目录 当目录不存在的时候创建文件,否则返回false */ @@ -409,9 +426,19 @@ public class FileUtil { * 获取SD卡目录列表 */ public static List getSDPathList(Context context) { - List paths; + List paths = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - paths = getVolumeList(context); + try { + paths = getVolumeList(context); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } } else { List mounts = readMountsFile(); List volds = readVoldFile(); @@ -471,16 +498,26 @@ public class FileUtil { /** * getSDPathList */ - private static List getVolumeList(final Context context) { + private static List getVolumeList(final Context context) + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException { List pathList = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { StorageManager manager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); List volumes = manager.getStorageVolumes(); + + Class volumeClass = StorageVolume.class; + Method getPath = volumeClass.getDeclaredMethod("getPath"); + //Method isRemovable = volumeClass.getDeclaredMethod("isRemovable"); + getPath.setAccessible(true); + //isRemovable.setAccessible(true); + for (StorageVolume volume : volumes) { String state = volume.getState(); + if (state.equals(Environment.MEDIA_MOUNTED)) { - pathList.add(volume.toString()); + pathList.add((String) getPath.invoke(volume)); } } } else { diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index d1839d15..aad5bc82 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -39,7 +39,7 @@ public class HttpDownloadModule extends BaseViewModule { //"http://202.98.201.103:7000/vrs/TPK/ZTC440402001Z.tpk"; private final String defFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() - + "/tttt.apk"; + + "/update.zip"; private MutableLiveData liveData = new MutableLiveData<>(); private DownloadEntity singDownloadInfo; @@ -51,10 +51,15 @@ public class HttpDownloadModule extends BaseViewModule { //String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl); //String url = // "http://sdkdown.muzhiwan.com/openfile/2019/05/21/com.netease.tom.mzw_5ce3ef8754d05.apk"; - String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; + //String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; + //String url = "http://fdfs.speedata.cn:9989/group1/M00/00/05/rBGFrl3fdAKAVJwfMtSa9R18wLU139.zip"; + //String url = "http://9.9.9.28:8088/files/update.zip"; + String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; //String url = "https://imtt.dd.qq.com/16891/apk/70BFFDB05AB8686F2A4CF3E07588A377.apk?fsname=com.tencent.tmgp.speedmobile_1.16.0.33877_1160033877.apk&csr=1bbd"; //String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; - String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); + //String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); + String filePath = "/mnt/sdcard/gg.png"; + //String filePath = "/mnt/sdcard/update.zip"; singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); if (singDownloadInfo == null) { diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 798cf527..d72aa415 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -39,6 +39,7 @@ import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; +import com.arialyy.aria.util.FileUtil; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; @@ -274,11 +275,13 @@ public class SingleTaskActivity extends BaseActivity { break; } if (Aria.download(this).load(mTaskId).isRunning()) { - Aria.download(this).load(mTaskId).stop(); + Aria.download(this) + .load(mTaskId) + .stop(); } else { mUrl = "http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk"; Aria.download(this).load(mTaskId) - .updateUrl(mUrl) + //.updateUrl(mUrl) .resume(); } break; @@ -297,6 +300,7 @@ public class SingleTaskActivity extends BaseActivity { .load(mUrl) .setFilePath(mFilePath, true) .option(option) + .ignoreCheckPermissions() .create(); } diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index 19042e5f..5f5aa6b5 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -35,9 +35,9 @@ public class UploadModule extends BaseViewModule { * 获取Ftp上传信息 */ LiveData getFtpInfo(Context context) { - String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.50:2121/aa/你好"); + String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aa/你好"); String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, - Environment.getExternalStorageDirectory().getPath() + "/Download/Folx P58.zip"); + Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db"); UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath); if (entity != null) { diff --git a/build.gradle b/build.gradle index 752aad52..2c64b34a 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 379 - versionName = '3.7.9' + versionCode = 380 + versionName = '3.7.10_pre_3' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName diff --git a/py/.vscode/launch.json b/py/.vscode/launch.json deleted file mode 100644 index b80908c1..00000000 --- a/py/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // 使用 IntelliSense 了解相关属性。 - // 悬停以查看现有属性的描述。 - // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python: 当前文件", - "type": "python", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal" - } - ] -} \ No newline at end of file diff --git a/py/.vscode/settings.json b/py/.vscode/settings.json deleted file mode 100644 index 4ca83c7f..00000000 --- a/py/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "python.linting.pylintEnabled": false, - "python.linting.pep8Enabled": true, - "python.linting.enabled": true, - "python.pythonPath": "/usr/local/bin/python3.7" -} \ No newline at end of file diff --git a/test/347/AndroidAria.db b/test/347/AndroidAria.db new file mode 100644 index 0000000000000000000000000000000000000000..ce6690b2a1c390540eff8ef99e7bef1d7947a616 GIT binary patch literal 73728 zcmeI*O>EoN0S9nWl4aSkt+<6jU`8+osZa}31nbMT>aDFT({51P@?@o5x)vTS(lTp_ zR7k2`vUUKSY#p}4&O5XPh7CLKFbu)4p+&bHci3$}fu+Zu*4?(--lHUnlc-MpJ6UCfdKO|&oc}&M4r!(XLq_l-h?^}G@K>R zna=V_FGEan_J#52N6ba`119>L@xMnm$G$oKi_zbW4M*;cn&EdM?+&#>%utg3Aoz!q zB6(`V?@mXDxm1dMUo^#?y1c9%sC7-0Dx$G3sHS4xIvO%rEDME-z*h=OYXX1tbAG0} zC+b_W$-h=87hfrq6ID%;`D+^$VO1z6_GD3#b%U=6->D=NW2@C{YPz|!$uE^kYeHco zq05q@%T@EbUUy>9<(nM=nt#hYW5;GOpz3|Zc;5n-jelqWPL@c%Nt@tcG9Tn zTB~V?bhlA4ihC_}UzVJ86l0aHMJb%Hefd^5eSWiiZM{&w!M`fpNVIy}%5Rh^{KoaQ zwZuxPEL>aNpeyh*PKt59EUXA+VWTK)IeJb?ZS)zEPMj}o@Ygq&DRr^1RV*wEG-O#= z6M7-uH0-}RCiYIGQYUjaMRU(r?D68oF!y?jWfWDCzi-^ElX|=@woJ`l-aeZ3+o_{r z&m4YZe3;8**gNleikQ92-ZJJ2iXL0YTq}@LrZt*%*_0~^&Dd+JB-cqvQ%QE4G{3i$ z+j2Lr64jQj%c{AlYc*XqjPB~Y-Ss+uP6|OMQYXi~CCV_hraL{u)SWcMx*{6R5-G#f z*;%gjm$?4&)28@;lplWJF%tC}vk-%og$ z-JkF?J9VwPzb30)ELoM-w5n(-niF)4m#c;(h@Ecs|BY#H$~DQUrJiciUxaDxYj5@p z%`E>#_ik`>n2X2R_Y0m<=PhvUr7;gew*f--6#xi3)~I%JKW8v zkLEGHcp=P{;)7~aJMLaZYA=u9S%?gCSFf@^zG4@l>rHRZ*uOmLDMtODJ_)^|XqsYm zuOX}Mu3I(9vfascv#whaoh+tb?N$`IE_Hf!R~z6dETliy3pIE2o>I_X_uGW+%y`J! z|I$d9+q!yEzW3+NcB((*!eJ>q%w4|B-c{P|r_&7e8l;FPzkA{QjAU0xPbH;hq%EzH z9--6M^w;Cw_lAC7kD+2L(RSIX)tzrC?KaXCT58Jb@-0G5r+dhbUL-w4^=*xdtH6>OIXcj*XwvcmnzQ`CMxu-K4q zJxxs|--ml&SmN_TVeZw~Njc(H>{gJr{P5uq0>j+mBKxCpPv2^ndYXCd)w&&NUvS8i zS0}9WB(#<%wQ`>N)ZeCg_P5itxq3f8ecF03%!!L9Wt%5~US{+>WF2tWV= z5P$##AOHafKmY;|I9meaWOLZ)zZvqt1OW&@00Izz00bZa0SG_<0uX?}c@Y?A16OYE zZ5KDTue>6vM*H~xVn(Hl>Vm1a_T=YC+}9P3#r*rLc&Hr zW5@#&1Rwwb2tWV=5P$##AOHafKmYgSb(1Y$MOF;&S2~s1Rwwb2tWV=5P$##AOHafK!6C)^Z$W~Uoz3x$P5z% zAOHafKmY;|fB*y_009U<;QuCIr2?btp_(<#O>yj?%h@AqH@+bMdOE3UnpBso;=Y`$ z$)>5OHI4l1X~oP{rF15no0sxCVmeuC?#I$A4b>!bL#m3pMBdc&W~)hFA1WGqtUVoh zZ@&pz&$DEk-?AqCY;*Wv>O-1XLwj3PYsrS9%2i$5HJkEwPD-cc)Ld>hH$OL@6bEgt zpBVbKl%D?&MC(lS9+_c+00bZa0SG_<0uX=z1Rwwb2teSm1=d2=G_nv7sjaa5YuC*o)Njp%DWTSO^ zPdO0PB#Dg#re{-m@-jP5XA5NJHxaVx14f_wxqf@lfcV3Bz`?0eoDySAQ=CjXIX_Qu ze*XJk(?Rn+9N$CdA4MqV7V@3agPD9sGsw!i$xaPTheA^$q#$v`YLT&6Qu0~#FQ5N$ zB%4nzWEwlcOOufh)oFkwHIX(DVPn@hv9$t%)Le!2|&aKmY;|fB*y_009U<00RC6 ztVVE>3ouj6vn|Pxz07v^e1u~-&SI^_zCE||us1t;xq;t-`u=w2q3;}s j`~Uf`*jN(+5P$##AOHafKmY;|fB*!Z4uSJ{|G)nMM^g}` literal 0 HcmV?d00001 From 3871d8f0568c0da2b2f51b9e9be6e0ef085a7888 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Tue, 3 Dec 2019 19:42:37 +0800 Subject: [PATCH 033/140] 3.7.10 --- .../com/arialyy/aria/core/AriaManager.java | 2 +- .../aria/core/common/AbsBuilderTarget.java | 11 ++++++- .../common/controller/FeatureController.java | 7 +++++ .../aria/core/download/CheckDEntityUtil.java | 2 +- .../aria/core/download/CheckDGEntityUtil.java | 7 ++--- .../aria/core/download/DownloadReceiver.java | 4 +-- .../download/target/DNormalConfigHandler.java | 2 +- .../download/target/FtpBuilderTarget.java | 3 ++ .../download/target/FtpDirBuilderTarget.java | 1 + .../download/target/FtpDirNormalTarget.java | 1 + .../core/download/target/FtpNormalTarget.java | 1 + .../download/target/GroupBuilderTarget.java | 3 +- .../download/target/GroupNormalTarget.java | 1 + .../download/target/HttpBuilderTarget.java | 12 ++++---- .../download/target/HttpNormalTarget.java | 1 + .../download/target/M3U8NormalTarget.java | 1 + .../download/target/TcpBuilderTarget.java | 15 +--------- .../aria/core/upload/CheckUEntityUtil.java | 6 ++-- .../core/upload/target/FtpBuilderTarget.java | 5 +++- .../core/upload/target/FtpNormalTarget.java | 1 + .../core/upload/target/HttpBuilderTarget.java | 8 +++-- .../core/upload/target/HttpNormalTarget.java | 1 + DEV_LOG.md | 3 +- .../arialyy/aria/ftp/FtpDirInfoThread.java | 2 +- .../aria/core/download/DTaskWrapper.java | 13 -------- .../aria/core/upload/UTaskWrapper.java | 13 -------- .../aria/core/wrapper/AbsTaskWrapper.java | 14 ++++++++- .../java/com/arialyy/aria/util/CheckUtil.java | 30 ++++++++++++++++--- .../com/arialyy/aria/util/DbDataHelper.java | 16 +++++++++- .../com/arialyy/aria/util/RecordUtil.java | 20 +++++++++++-- README.md | 25 +++++++++------- .../download/group/DownloadGroupActivity.java | 2 +- build.gradle | 2 +- 33 files changed, 149 insertions(+), 86 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index 0d0c2f4a..b21910c6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -282,7 +282,7 @@ import java.util.concurrent.ConcurrentHashMap; RecordUtil.delTaskRecord(key, IRecordHandler.TYPE_DOWNLOAD, removeFile, true); break; case 2: - RecordUtil.delGroupTaskRecord(key, removeFile); + RecordUtil.delGroupTaskRecordByHash(key, removeFile); break; case 3: RecordUtil.delTaskRecord(key, IRecordHandler.TYPE_UPLOAD); diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java index 4ce228e3..16eb20c8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java @@ -20,7 +20,7 @@ import com.arialyy.aria.core.common.controller.IStartFeature; import com.arialyy.aria.core.inf.AbsTarget; /** - * 处理第一次下载 + * 处理第一次创建的任务 */ public abstract class AbsBuilderTarget extends AbsTarget implements IStartFeature { @@ -42,6 +42,15 @@ public abstract class AbsBuilderTarget extends return (TARGET) this; } + /** + * 忽略文件占用,不管文件路径是否被其它任务占用,都执行上传\下载任务 + * 需要注意的是:如果文件被其它任务占用,并且还调用了该方法,将自动删除占用了该文件路径的任务 + */ + public TARGET ignoreFilePathOccupy() { + getController().ignoreFilePathOccupy(); + return (TARGET) this; + } + /** * 添加任务 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java index 1ceab180..875d2ad4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java @@ -98,6 +98,13 @@ public abstract class FeatureController { this.ignoreCheckPermissions = true; } + /** + * 强制执行任务,不管文件路径是否被占用 + */ + public void ignoreFilePathOccupy() { + mTaskWrapper.setIgnoreFilePathOccupy(true); + } + protected AbsTaskWrapper getTaskWrapper() { return mTaskWrapper; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index c6eb5dcf..163af4ee 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -134,7 +134,7 @@ public class CheckDEntityUtil implements ICheckEntityUtil { //设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径 if (!filePath.equals(mEntity.getFilePath())) { // 检查路径冲突 - if (!CheckUtil.checkDownloadPathConflicts(mWrapper.isForceDownload(), filePath)) { + if (!CheckUtil.checkDPathConflicts(mWrapper.isIgnoreFilePathOccupy(), filePath)) { return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 402b1846..0e854888 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -16,12 +16,12 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; -import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; import java.io.File; @@ -70,10 +70,9 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { return false; } - if ((mEntity.getDirPath() == null || !mEntity.getDirPath().equals(mWrapper.getDirPathTemp())) - && DbEntity.checkDataExist(DownloadGroupEntity.class, "dirPath=?", + // 检查路径冲突 + if (mWrapper.isNewTask() && !CheckUtil.checkDGPathConflicts(mWrapper.isIgnoreFilePathOccupy(), mWrapper.getDirPathTemp())) { - ALog.e(TAG, String.format("文件夹路径【%s】已被其它任务占用,请重新设置文件夹路径", mWrapper.getDirPathTemp())); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index cbb6c5e8..446a92e3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -291,7 +291,7 @@ public class DownloadReceiver extends AbsReceiver { if (CheckUtil.checkDownloadUrlsIsEmpty(urls)) { return null; } - return DbDataHelper.getDGEntity(CommonUtil.getMd5Code(urls)); + return DbDataHelper.getDGEntityByHash(CommonUtil.getMd5Code(urls)); } /** @@ -304,7 +304,7 @@ public class DownloadReceiver extends AbsReceiver { if (!CheckUtil.checkUrl(url)) { return null; } - return DbDataHelper.getDGEntity(url); + return DbDataHelper.getDGEntityByHash(url); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java index c5e24802..7f440895 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/DNormalConfigHandler.java @@ -93,7 +93,7 @@ class DNormalConfigHandler implements IConfigHandler { } void setForceDownload(boolean forceDownload) { - getWrapper().setForceDownload(forceDownload); + getWrapper().setIgnoreFilePathOccupy(forceDownload); } void setUrl(String url) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 6bb56047..047df49b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -34,6 +34,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { mConfigHandler = new DNormalConfigHandler<>(this, -1); mConfigHandler.setUrl(url); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); + getTaskWrapper().setNewTask(true); } /** @@ -70,7 +71,9 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * * @param filePath 路径必须为文件路径,不能为文件夹路径 * @param forceDownload {@code true}强制下载,不考虑文件路径是否被占用 + * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ + @Deprecated @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { mConfigHandler.setTempFilePath(filePath); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index 3b6cad55..488f81a1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -35,6 +35,7 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { this.url = url; mConfigHandler = new FtpDirConfigHandler<>(this, -1); getEntity().setGroupHash(url); + getTaskWrapper().setNewTask(true); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index a6fae105..8ce740fe 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -32,6 +32,7 @@ public class FtpDirNormalTarget extends AbsNormalTarget { FtpDirNormalTarget(long taskId) { mConfigHandler = new FtpDirConfigHandler<>(this, taskId); + getTaskWrapper().setNewTask(false); } @Override public boolean isRunning() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index 1513e408..c10f646b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -34,6 +34,7 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new DNormalConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); + getTaskWrapper().setNewTask(false); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index 2a93fd04..cbd7edae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -34,8 +34,9 @@ public class GroupBuilderTarget extends AbsBuilderTarget { GroupBuilderTarget(List urls) { mConfigHandler = new HttpGroupConfigHandler<>(this, -1); - getTaskWrapper().setRequestType(ITaskWrapper.DG_HTTP); mConfigHandler.setGroupUrl(urls); + getTaskWrapper().setRequestType(ITaskWrapper.DG_HTTP); + getTaskWrapper().setNewTask(true); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 5c3be712..00b4d8f6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -33,6 +33,7 @@ public class GroupNormalTarget extends AbsNormalTarget { GroupNormalTarget(long taskId) { mConfigHandler = new HttpGroupConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(ITaskWrapper.DG_HTTP); + getTaskWrapper().setNewTask(false); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index eed6b2b3..ae3fd203 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -22,12 +22,9 @@ import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; import com.arialyy.aria.core.download.m3u8.M3U8VodOption; -import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.util.CheckUtil; public class HttpBuilderTarget extends AbsBuilderTarget { @@ -35,13 +32,14 @@ public class HttpBuilderTarget extends AbsBuilderTarget { HttpBuilderTarget(String url) { mConfigHandler = new DNormalConfigHandler<>(this, -1); - getTaskWrapper().setRequestType(ITaskWrapper.D_HTTP); mConfigHandler.setUrl(url); + getTaskWrapper().setRequestType(ITaskWrapper.D_HTTP); + getTaskWrapper().setNewTask(true); } @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { - if (m3U8VodOption == null){ + if (m3U8VodOption == null) { throw new NullPointerException("m3u8任务设置为空"); } getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_VOD); @@ -52,7 +50,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget m3u8LiveOption(M3U8LiveOption m3U8LiveOption) { - if (m3U8LiveOption == null){ + if (m3U8LiveOption == null) { throw new NullPointerException("m3u8任务设置为空"); } getTaskWrapper().setRequestType(AbsTaskWrapper.M3U8_LIVE); @@ -92,7 +90,9 @@ public class HttpBuilderTarget extends AbsBuilderTarget { * * @param filePath 路径必须为文件路径,不能为文件夹路径 * @param forceDownload {@code true}强制下载,不考虑文件路径是否被占用 + * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ + @Deprecated @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { mConfigHandler.setTempFilePath(filePath); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java index d17e9b3a..ea7800d0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java @@ -35,6 +35,7 @@ public class HttpNormalTarget extends AbsNormalTarget { HttpNormalTarget(long taskId) { mConfigHandler = new DNormalConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(getTaskWrapper().getEntity().getTaskType()); + getTaskWrapper().setNewTask(false); } @CheckResult(suggest = Suggest.TASK_CONTROLLER) diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java index 2b530b85..63a8cfcd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/M3U8NormalTarget.java @@ -27,6 +27,7 @@ public class M3U8NormalTarget extends AbsNormalTarget { M3U8NormalTarget(DTaskWrapper wrapper) { setTaskWrapper(wrapper); + getTaskWrapper().setNewTask(false); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java index 4b492cb0..314294d4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -33,6 +33,7 @@ public class TcpBuilderTarget extends AbsBuilderTarget { TcpBuilderTarget(String ip, int port) { mConfigHandler = new DNormalConfigHandler<>(this, -1); getTaskWrapper().setRequestType(ITaskWrapper.D_TCP); + getTaskWrapper().setNewTask(true); } // ///** @@ -56,18 +57,4 @@ public class TcpBuilderTarget extends AbsBuilderTarget { return this; } - /** - * 设置文件存储路径,如果需要修改新的文件名,修改路径便可。 - * 如:原文件路径 /mnt/sdcard/test.zip - * 如果需要将test.zip改为game.zip,只需要重新设置文件路径为:/mnt/sdcard/game.zip - * - * @param filePath 路径必须为文件路径,不能为文件夹路径 - * @param forceDownload {@code true}强制下载,不考虑文件路径是否被占用 - */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public TcpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { - mConfigHandler.setTempFilePath(filePath); - mConfigHandler.setForceDownload(forceDownload); - return this; - } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java index c29c9efe..40e891f8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/CheckUEntityUtil.java @@ -58,9 +58,9 @@ public class CheckUEntityUtil implements ICheckEntityUtil { ALog.e(TAG, "上传失败,文件路径【" + filePath + "】不合法"); return false; } - - // 检查路径冲突 - if (!CheckUtil.checkUploadPathConflicts(mWrapper.isForceUpload(), filePath)) { + // 任务是新任务,并且路径冲突就不会继续执行 + if (mWrapper.isNewTask() && !CheckUtil.checkUPathConflicts( + mWrapper.isIgnoreFilePathOccupy(), filePath)) { return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index 741dd2d8..12546fd3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -52,9 +52,12 @@ public class FtpBuilderTarget extends AbsBuilderTarget { /** * 如果文件路径被其它任务占用,删除其它任务 + * + * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ + @Deprecated public FtpBuilderTarget forceUpload() { - ((UTaskWrapper)getTaskWrapper()).setForceUpload(true); + getTaskWrapper().setIgnoreFilePathOccupy(true); return this; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index e8353f9d..d093d8ad 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -33,6 +33,7 @@ public class FtpNormalTarget extends AbsNormalTarget { FtpNormalTarget(long taskId) { mConfigHandler = new UNormalConfigHandler<>(this, taskId); getTaskWrapper().setRequestType(ITaskWrapper.U_FTP); + getTaskWrapper().setNewTask(false); } /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 8a72c40c..3e7e1fb5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -19,7 +19,6 @@ import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -30,12 +29,12 @@ public class HttpBuilderTarget extends AbsBuilderTarget { private UNormalConfigHandler mConfigHandler; HttpBuilderTarget(String filePath) { - mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); //http暂时不支持断点上传 getTaskWrapper().setSupportBP(false); getTaskWrapper().setRequestType(AbsTaskWrapper.U_HTTP); + getTaskWrapper().setNewTask(true); } /** @@ -64,9 +63,12 @@ public class HttpBuilderTarget extends AbsBuilderTarget { /** * 如果文件路径被其它任务占用,删除其它任务 + * + * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ + @Deprecated public HttpBuilderTarget forceUpload() { - ((UTaskWrapper) getTaskWrapper()).setForceUpload(true); + getTaskWrapper().setIgnoreFilePathOccupy(true); return this; } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index 27a6e1fa..66136b04 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -33,6 +33,7 @@ public class HttpNormalTarget extends AbsNormalTarget { mConfigHandler = new UNormalConfigHandler<>(this, taskId); getTaskWrapper().setSupportBP(false); getTaskWrapper().setRequestType(AbsTaskWrapper.U_HTTP); + getTaskWrapper().setNewTask(false); } /** diff --git a/DEV_LOG.md b/DEV_LOG.md index be51081d..e000b3e9 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,10 +1,11 @@ ## 开发日志 - + v_3.7.10 + + v_3.7.10 (2019/12/3) - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 - fix bug https://github.com/AriaLyy/Aria/issues/542 - fix bug https://github.com/AriaLyy/Aria/issues/547 - 修复下载失败时,中断重试无效的问题 - 增加忽略权限检查的api,`ignoreCheckPermissions()` + - 增加通用的的忽略文件路径被占用的api,`isIgnoreFilePathOccupy()` + v_3.7.9 (2019/11/28) - fix bug https://github.com/AriaLyy/Aria/issues/537 + v_3.7.8 (2019/11/28) diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java index 5bdb7614..7954d187 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java @@ -74,7 +74,7 @@ public class FtpDirInfoThread extends AbsFtpInfoThread { */ private String mTempFilePath; - /** - * {@code true}强制下载,不考虑文件路径是否被占用 - */ - private boolean forceDownload = false; - public DTaskWrapper(DownloadEntity entity) { super(entity); } @@ -124,12 +119,4 @@ public class DTaskWrapper extends AbsTaskWrapper { public void setTempFilePath(String mTempFilePath) { this.mTempFilePath = mTempFilePath; } - - public boolean isForceDownload() { - return forceDownload; - } - - public void setForceDownload(boolean forceDownload) { - this.forceDownload = forceDownload; - } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java index 5ab218d8..42e5d4c8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UTaskWrapper.java @@ -29,11 +29,6 @@ public class UTaskWrapper extends AbsTaskWrapper { */ private String tempUrl; - /** - * 如果文件路径被其它任务占用,设置为true时,删除其它任务 - */ - private boolean forceUpload = false; - public UTaskWrapper(UploadEntity entity) { super(entity); } @@ -53,14 +48,6 @@ public class UTaskWrapper extends AbsTaskWrapper { return getEntity().getKey(); } - public boolean isForceUpload() { - return forceUpload; - } - - public void setForceUpload(boolean forceUpload) { - this.forceUpload = forceUpload; - } - @Override public UploadConfig getConfig() { return Configuration.getInstance().uploadCfg; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java index 3e489475..cf1a3fd7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/AbsTaskWrapper.java @@ -24,7 +24,6 @@ import com.arialyy.aria.core.event.ErrorEvent; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.ITaskOption; import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ComponentUtil; /** @@ -85,6 +84,19 @@ public abstract class AbsTaskWrapper */ private TaskOptionParams optionParams = new TaskOptionParams(); + /** + * {@code true}强制下载\上传,不考虑文件路径是否被占用 + */ + private boolean ignoreFilePathOccupy = false; + + public boolean isIgnoreFilePathOccupy() { + return ignoreFilePathOccupy; + } + + public void setIgnoreFilePathOccupy(boolean ignoreFilePathOccupy) { + this.ignoreFilePathOccupy = ignoreFilePathOccupy; + } + public void setTaskOption(ITaskOption option) { this.taskOption = option; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 7ba5a234..48bbb226 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -18,6 +18,7 @@ package com.arialyy.aria.util; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; @@ -32,13 +33,13 @@ public class CheckUtil { private static final String TAG = "CheckUtil"; /** - * 检查和处理路径冲突 + * 检查和处理下载任务的路径冲突 * * @param isForceDownload true,如果路径冲突,将删除其它任务的记录的 * @param filePath 文件保存路径 * @return false 任务不再执行,true 任务继续执行 */ - public static boolean checkDownloadPathConflicts(boolean isForceDownload, String filePath) { + public static boolean checkDPathConflicts(boolean isForceDownload, String filePath) { if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) { if (!isForceDownload) { ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用,请设置其它保存路径", filePath)); @@ -53,13 +54,13 @@ public class CheckUtil { } /** - * 检查和处理路径冲突 + * 检查和处理上传任务的路径冲突 * * @param isForceUpload true,如果路径冲突,将删除其它任务的记录的 * @param filePath 文件保存路径 * @return false 任务不再执行,true 任务继续执行 */ - public static boolean checkUploadPathConflicts(boolean isForceUpload, String filePath) { + public static boolean checkUPathConflicts(boolean isForceUpload, String filePath) { if (DbEntity.checkDataExist(UploadEntity.class, "filePath=?", filePath)) { if (!isForceUpload) { ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用,请设置其它文件路径", filePath)); @@ -73,6 +74,27 @@ public class CheckUtil { return true; } + /** + * 检查和处理组合任务的路径冲突 + * + * @param isForceDownload true,如果路径冲突,将删除其它任务的记录的 + * @param dirPath 文件保存路径 + * @return false 任务不再执行,true 任务继续执行 + */ + public static boolean checkDGPathConflicts(boolean isForceDownload, String dirPath) { + if (DbEntity.checkDataExist(DownloadGroupEntity.class, "dirPath=?", dirPath)) { + if (!isForceDownload) { + ALog.e(TAG, String.format("下载失败,文件夹路径【%s】已经被其它任务占用,请设置其它文件路径", dirPath)); + return false; + } else { + ALog.w(TAG, String.format("文件夹路径【%s】已经被其它任务占用,当前任务将覆盖该路径", dirPath)); + RecordUtil.delGroupTaskRecordByPath(dirPath, false); + return true; + } + } + return true; + } + /** * 检查成员类是否是静态和public */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java index d9382dd4..94776ad1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java @@ -52,7 +52,7 @@ public class DbDataHelper { * @param groupHash 组合任务Hash * @return 实体不存在,返回null */ - public static DownloadGroupEntity getDGEntity(String groupHash) { + public static DownloadGroupEntity getDGEntityByHash(String groupHash) { List wrapper = DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.groupHash=?", groupHash); @@ -60,6 +60,20 @@ public class DbDataHelper { return wrapper == null || wrapper.size() == 0 ? null : wrapper.get(0).groupEntity; } + /** + * 获取组合任务实体、ftpDir任务实体 + * + * @param dirPath 组合任务Hash + * @return 实体不存在,返回null + */ + public static DownloadGroupEntity getDGEntityByPath(String dirPath) { + List wrapper = + DbEntity.findRelationData(DGEntityWrapper.class, "DownloadGroupEntity.dirPath=?", + dirPath); + + return wrapper == null || wrapper.size() == 0 ? null : wrapper.get(0).groupEntity; + } + /** * 获取组合任务实体、ftpDir任务实体 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index ceddf62c..dfc7ff7a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -42,12 +42,28 @@ public class RecordUtil { * @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件; * {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。 */ - public static void delGroupTaskRecord(String groupHash, boolean removeFile) { + public static void delGroupTaskRecordByHash(String groupHash, boolean removeFile) { if (TextUtils.isEmpty(groupHash)) { ALog.e(TAG, "删除下载任务组记录失败,groupHash为null"); return; } - DownloadGroupEntity groupEntity = DbDataHelper.getDGEntity(groupHash); + DownloadGroupEntity groupEntity = DbDataHelper.getDGEntityByHash(groupHash); + + delGroupTaskRecord(groupEntity, removeFile, true); + } + + /** + * 根据路径删除组合任务记录 + * + * @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件; + * {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。 + */ + public static void delGroupTaskRecordByPath(String dirPath, boolean removeFile) { + if (TextUtils.isEmpty(dirPath)) { + ALog.e(TAG, "删除下载任务组记录失败,组合任务路径为空"); + return; + } + DownloadGroupEntity groupEntity = DbDataHelper.getDGEntityByPath(dirPath); delGroupTaskRecord(groupEntity, removeFile, true); } diff --git a/README.md b/README.md index 20cbf846..05c0779b 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.9-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.9-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.9-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.9-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.7.10-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.7.10-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.10-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.10-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.9' -annotationProcessor 'com.arialyy.aria:compiler:3.7.9' -implementation 'com.arialyy.aria:ftpComponent:3.7.9' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.9' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.10' +annotationProcessor 'com.arialyy.aria:compiler:3.7.10' +implementation 'com.arialyy.aria:ftpComponent:3.7.10' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.10' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,8 +137,13 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.9 (2019/11/28) - - fix bug https://github.com/AriaLyy/Aria/issues/537 + + v_3.7.10 (2019/12/3) + - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 + - fix bug https://github.com/AriaLyy/Aria/issues/542 + - fix bug https://github.com/AriaLyy/Aria/issues/547 + - 修复下载失败时,中断重试无效的问题 + - 增加忽略权限检查的api,`ignoreCheckPermissions()` + - 增加通用的的忽略文件路径被占用的api,`isIgnoreFilePathOccupy()` [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index a1446e59..998f1ed5 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -96,11 +96,11 @@ public class DownloadGroupActivity extends BaseActivity Date: Sun, 8 Dec 2019 11:08:58 +0800 Subject: [PATCH 034/140] =?UTF-8?q?=E7=A7=BB=E9=99=A4androidx=E3=80=81supp?= =?UTF-8?q?ort=E4=BE=9D=E8=B5=96=EF=BC=8C=E7=8E=B0=E5=9C=A8=E5=93=AA?= =?UTF-8?q?=E4=B8=AA=E7=89=88=E6=9C=AC=E7=9A=84appcompat=E5=BA=93=E9=83=BD?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E5=AF=BC=E5=85=A5Aria=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=9C=A8xml=E4=B8=AD=E4=BD=BF=E7=94=A8fragme?= =?UTF-8?q?nt=E5=AF=BC=E8=87=B4=E7=9A=84=E5=86=85=E5=AD=98=E6=B3=84?= =?UTF-8?q?=E6=BC=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Aria/build.gradle | 2 +- .../com/arialyy/aria/core/AriaManager.java | 57 ++++++++----------- .../arialyy/aria/core/common/FtpOption.java | 2 +- .../arialyy/aria/core/common/HttpOption.java | 10 +--- .../aria/core/download/DownloadReceiver.java | 21 +++---- .../target/AbsGroupConfigHandler.java | 4 -- .../download/target/FtpBuilderTarget.java | 10 +--- .../download/target/FtpDirBuilderTarget.java | 6 -- .../download/target/FtpDirNormalTarget.java | 5 -- .../core/download/target/FtpNormalTarget.java | 8 +-- .../download/target/GroupBuilderTarget.java | 10 ---- .../download/target/GroupNormalTarget.java | 8 --- .../download/target/HttpBuilderTarget.java | 12 +--- .../target/HttpGroupConfigHandler.java | 3 - .../download/target/HttpNormalTarget.java | 8 --- .../download/target/TcpBuilderTarget.java | 8 +-- .../com/arialyy/aria/core/inf/AbsTarget.java | 5 +- .../com/arialyy/aria/core/inf/IReceiver.java | 2 +- .../arialyy/aria/core/inf/ReceiverType.java | 11 +--- .../aria/core/manager/TaskWrapperManager.java | 4 +- .../aria/core/upload/UploadReceiver.java | 14 ++--- .../core/upload/target/FtpBuilderTarget.java | 5 -- .../core/upload/target/FtpNormalTarget.java | 3 - .../core/upload/target/HttpBuilderTarget.java | 4 -- .../core/upload/target/HttpNormalTarget.java | 4 -- DEV_LOG.md | 3 + FtpComponent/build.gradle | 2 - .../aria/ftp/upload/FtpFISAdapter.java | 9 ++- HttpComponent/build.gradle | 4 -- M3U8Component/build.gradle | 1 - PublicComponent/build.gradle | 1 - .../com/arialyy/aria/core/FtpUrlEntity.java | 1 - .../com/arialyy/aria/core/ProtocolType.java | 15 +---- .../arialyy/aria/core/config/ConfigType.java | 12 +--- .../arialyy/aria/core/config/XMLReader.java | 2 +- .../aria/core/inf/TaskSchedulerType.java | 13 +---- .../aria/core/processor/ITsMergeHandler.java | 3 +- .../com/arialyy/aria/core/task/AbsTask.java | 7 +-- .../com/arialyy/aria/core/task/ITask.java | 6 +- .../aria/core/task/IThreadTaskObserver.java | 5 +- .../arialyy/aria/core/task/ThreadTask.java | 7 +-- .../com/arialyy/aria/util/SSLContextUtil.java | 12 ++-- .../com/arialyy/aria/util/WeakHandler.java | 35 ++++++------ SFtpComponent/build.gradle | 1 - .../simple/core/upload/UploadModule.java | 2 +- 45 files changed, 98 insertions(+), 269 deletions(-) diff --git a/Aria/build.gradle b/Aria/build.gradle index 14de1143..ea5fe579 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -23,7 +23,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation 'junit:junit:4.12' - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" +// implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" api project(':AriaAnnotations') api project(path: ':PublicComponent') api project(path: ':HttpComponent') diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index b21910c6..a562e53b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -290,19 +290,31 @@ import java.util.concurrent.ConcurrentHashMap; } } - private IReceiver putReceiver(@ReceiverType String type, Object obj) { + private IReceiver putReceiver(String type, Object obj) { final String key = getKey(type, obj); IReceiver receiver = mReceivers.get(key); boolean needRmReceiver = false; // 监控Dialog、fragment、popupWindow的生命周期 final WidgetLiftManager widgetLiftManager = new WidgetLiftManager(); + Context subParenActivity = null; + if (obj instanceof Dialog) { needRmReceiver = widgetLiftManager.handleDialogLift((Dialog) obj); + subParenActivity = ((Dialog) obj).getOwnerActivity(); } else if (obj instanceof PopupWindow) { needRmReceiver = widgetLiftManager.handlePopupWindowLift((PopupWindow) obj); + subParenActivity = ((PopupWindow) obj).getContentView().getContext(); } else if (isDialogFragment(obj.getClass())) { needRmReceiver = widgetLiftManager.handleDialogFragmentLift(getDialog(obj)); + subParenActivity = getFragmentActivity(obj); + } else if (isFragment(obj.getClass())) { + subParenActivity = getFragmentActivity(obj); + } + + if (subParenActivity instanceof Activity) { + relateSubClass(type, obj, (Activity) subParenActivity); } + if (receiver == null) { AbsReceiver absReceiver = type.equals(ReceiverType.DOWNLOAD) ? new DownloadReceiver() : new UploadReceiver(); @@ -315,30 +327,6 @@ import java.util.concurrent.ConcurrentHashMap; return receiver; } - /** - * 根据功能类型和控件类型获取对应的key - * - * @param type {@link ReceiverType} - * @param obj 观察者对象 - * @return {@link #createKey(String, Object)} - */ - private String getKey(@ReceiverType String type, Object obj) { - if (isFragment(obj.getClass())) { - relateSubClass(type, obj, getFragmentActivity(obj)); - } else if (obj instanceof Dialog) { - Activity activity = ((Dialog) obj).getOwnerActivity(); - if (activity != null) { - relateSubClass(type, obj, activity); - } - } else if (obj instanceof PopupWindow) { - Context context = ((PopupWindow) obj).getContentView().getContext(); - if (context instanceof Activity) { - relateSubClass(type, obj, (Activity) context); - } - } - return createKey(type, obj); - } - /** * 获取fragment的activity * @@ -421,14 +409,17 @@ import java.util.concurrent.ConcurrentHashMap; * @param sub Fragment或dialog类 * @param activity activity寄主类 */ - private void relateSubClass(@ReceiverType String type, Object sub, Activity activity) { - String key = createKey(type, activity); - List list = mSubClass.get(key); - if (list == null) { - list = new ArrayList<>(); - mSubClass.put(key, list); + private void relateSubClass(String type, Object sub, Activity activity) { + String key = getKey(type, activity); + List subClass = mSubClass.get(key); + if (subClass == null) { + subClass = new ArrayList<>(); + mSubClass.put(key, subClass); + } + subClass.add(getKey(type, sub)); + if (mReceivers.get(key) == null) { // 将activity填充进去 + mReceivers.put(key, new DownloadReceiver()); } - list.add(createKey(type, sub)); } /** @@ -439,7 +430,7 @@ import java.util.concurrent.ConcurrentHashMap; * @return key的格式为:{@code String.format("%s_%s_%s", obj.getClass().getName(), type, * obj.hashCode());} */ - private String createKey(@ReceiverType String type, Object obj) { + private String getKey(String type, Object obj) { return String.format("%s_%s_%s", obj.getClass().getName(), type, obj.hashCode()); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java index 6c737d39..168f195e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java @@ -70,7 +70,7 @@ public class FtpOption extends BaseOption { * * @param protocol {@link ProtocolType} */ - public FtpOption setProtocol(@ProtocolType String protocol) { + public FtpOption setProtocol(String protocol) { if (TextUtils.isEmpty(protocol)) { ALog.e(TAG, "设置协议失败,协议信息为空"); return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java index e52c3866..16f7d4e2 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/HttpOption.java @@ -16,11 +16,6 @@ package com.arialyy.aria.core.common; import android.text.TextUtils; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; -import com.arialyy.aria.core.download.target.HttpBuilderTarget; -import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; @@ -50,7 +45,6 @@ public class HttpOption extends BaseOption { * * @param requestEnum {@link RequestEnum} */ - @CheckResult(suggest = Suggest.TO_CONTROLLER) public HttpOption setRequestType(RequestEnum requestEnum) { this.requestEnum = requestEnum; return this; @@ -97,7 +91,7 @@ public class HttpOption extends BaseOption { * @param key header对应的key * @param value header对应的value */ - public HttpOption addHeader(@NonNull String key, @NonNull String value) { + public HttpOption addHeader(String key, String value) { if (TextUtils.isEmpty(key)) { ALog.w(TAG, "设置header失败,header对应的key不能为null"); return this; @@ -118,7 +112,7 @@ public class HttpOption extends BaseOption { * * @param headers 一组http header数据 */ - public HttpOption addHeaders(@NonNull Map headers) { + public HttpOption addHeaders(Map headers) { if (headers.size() == 0) { ALog.w(TAG, "设置header失败,map没有header数据"); return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 446a92e3..09c58c08 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -15,8 +15,6 @@ */ package com.arialyy.aria.core.download; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; @@ -74,8 +72,7 @@ public class DownloadReceiver extends AbsReceiver { * * @param url 下载地址 */ - @CheckResult - public HttpBuilderTarget load(@NonNull String url) { + public HttpBuilderTarget load(String url) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); return DTargetFactory.getInstance() .generateBuilderTarget(HttpBuilderTarget.class, url); @@ -87,7 +84,6 @@ public class DownloadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * AbsEntity#getId()}读取任务id */ - @CheckResult public HttpNormalTarget load(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); return DTargetFactory.getInstance() @@ -99,7 +95,6 @@ public class DownloadReceiver extends AbsReceiver { * * @param urls 组合任务只任务列被,如果任务组的中的下载地址改变了,则任务从新的一个任务组 */ - @CheckResult public GroupBuilderTarget loadGroup(List urls) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); return DTargetFactory.getInstance().generateGroupBuilderTarget(urls); @@ -111,7 +106,7 @@ public class DownloadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * * AbsEntity#getId()}读取任务id */ - @CheckResult + public GroupNormalTarget loadGroup(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); return DTargetFactory.getInstance() @@ -121,8 +116,8 @@ public class DownloadReceiver extends AbsReceiver { /** * 加载ftp单任务下载地址,用于任务第一次下载,如果需要控制任务停止或删除等操作,请使用{@link #loadFtp(long)} */ - @CheckResult - public FtpBuilderTarget loadFtp(@NonNull String url) { + + public FtpBuilderTarget loadFtp(String url) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); return DTargetFactory.getInstance() .generateBuilderTarget(FtpBuilderTarget.class, url); @@ -134,7 +129,7 @@ public class DownloadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * AbsEntity#getId()}读取任务id */ - @CheckResult + public FtpNormalTarget loadFtp(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); return DTargetFactory.getInstance() @@ -144,8 +139,8 @@ public class DownloadReceiver extends AbsReceiver { /** * 加载ftp文件夹下载地址,用于任务第一次下载,如果需要控制任务停止或删除等操作,请使用{@link #loadFtpDir(long)} */ - @CheckResult - public FtpDirBuilderTarget loadFtpDir(@NonNull String dirUrl) { + + public FtpDirBuilderTarget loadFtpDir(String dirUrl) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); return DTargetFactory.getInstance().generateDirBuilderTarget(dirUrl); } @@ -156,7 +151,7 @@ public class DownloadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * AbsEntity#getId()}读取任务id */ - @CheckResult + public FtpDirNormalTarget loadFtpDir(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); return DTargetFactory.getInstance() diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java index 617848f6..3eec7170 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/AbsGroupConfigHandler.java @@ -16,9 +16,7 @@ package com.arialyy.aria.core.download.target; import android.text.TextUtils; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; -import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.event.ErrorEvent; @@ -66,7 +64,6 @@ abstract class AbsGroupConfigHandler implements IConfi * * @return 子任务管理器 */ - @CheckResult SubTaskManager getSubTaskManager() { if (mSubTaskManager == null) { mSubTaskManager = new SubTaskManager(getTaskWrapper()); @@ -89,7 +86,6 @@ abstract class AbsGroupConfigHandler implements IConfi return task != null && task.isRunning(); } - @CheckResult TARGET setDirPath(String dirPath) { mWrapper.setDirPathTemp(dirPath); return mTarget; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index 047df49b..a67a8bb7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -15,12 +15,9 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -40,7 +37,6 @@ public class FtpBuilderTarget extends AbsBuilderTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); @@ -56,8 +52,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * 1、如果保存路径是该文件的保存路径,如:/mnt/sdcard/file.zip,则使用路径中的文件名file.zip * 2、如果保存路径是文件夹路径,如:/mnt/sdcard/,则使用FTP服务器该文件的文件名 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpBuilderTarget setFilePath(@NonNull String filePath) { + public FtpBuilderTarget setFilePath(String filePath) { int lastIndex = mConfigHandler.getUrl().lastIndexOf("/"); getEntity().setFileName(mConfigHandler.getUrl().substring(lastIndex + 1)); mConfigHandler.setTempFilePath(filePath); @@ -74,8 +69,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ @Deprecated - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { + public FtpBuilderTarget setFilePath(String filePath, boolean forceDownload) { mConfigHandler.setTempFilePath(filePath); mConfigHandler.setForceDownload(forceDownload); return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java index 488f81a1..efd29d3a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirBuilderTarget.java @@ -15,11 +15,9 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -57,7 +55,6 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { * * @param dirPath 任务组保存文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpDirBuilderTarget setDirPath(String dirPath) { return mConfigHandler.setDirPath(dirPath); } @@ -65,7 +62,6 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { /** * 设置任务组别名 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpDirBuilderTarget setGroupAlias(String alias) { mConfigHandler.setGroupAlias(alias); return this; @@ -74,7 +70,6 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpDirBuilderTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); @@ -93,7 +88,6 @@ public class FtpDirBuilderTarget extends AbsBuilderTarget { * * @return 子任务管理器 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public SubTaskManager getSubTaskManager() { return mConfigHandler.getSubTaskManager(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index 8ce740fe..f221a394 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -15,11 +15,9 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -62,7 +60,6 @@ public class FtpDirNormalTarget extends AbsNormalTarget { * * @param dirPath 任务组保存文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpDirNormalTarget modifyDirPath(String dirPath) { return mConfigHandler.setDirPath(dirPath); } @@ -70,7 +67,6 @@ public class FtpDirNormalTarget extends AbsNormalTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpDirNormalTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); @@ -89,7 +85,6 @@ public class FtpDirNormalTarget extends AbsNormalTarget { * * @return 子任务管理器 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public SubTaskManager getSubTaskManager() { return mConfigHandler.getSubTaskManager(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java index c10f646b..21b4f2e3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpNormalTarget.java @@ -15,12 +15,9 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -40,7 +37,6 @@ public class FtpNormalTarget extends AbsNormalTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpNormalTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); @@ -56,8 +52,7 @@ public class FtpNormalTarget extends AbsNormalTarget { * 1、如果保存路径是该文件的保存路径,如:/mnt/sdcard/file.zip,则使用路径中的文件名file.zip * 2、如果保存路径是文件夹路径,如:/mnt/sdcard/,则使用FTP服务器该文件的文件名 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public FtpNormalTarget modifyFilePath(@NonNull String filePath) { + public FtpNormalTarget modifyFilePath(String filePath) { int lastIndex = mConfigHandler.getUrl().lastIndexOf("/"); getEntity().setFileName(mConfigHandler.getUrl().substring(lastIndex + 1)); mConfigHandler.setTempFilePath(filePath); @@ -67,7 +62,6 @@ public class FtpNormalTarget extends AbsNormalTarget { /** * 更新下载地址 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpNormalTarget updateUrl(String newUrl) { return mConfigHandler.updateUrl(newUrl); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java index cbd7edae..61968de4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupBuilderTarget.java @@ -15,11 +15,9 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DGTaskWrapper; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; @@ -42,7 +40,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget option(HttpOption option) { if (option == null) { throw new NullPointerException("任务配置为空"); @@ -59,7 +56,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * * @param fileSize 任务组总大小 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget setFileSize(long fileSize) { if (fileSize <= 0) { ALog.e(TAG, "文件大小不能小于 0"); @@ -78,7 +74,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * 2、如果你的知道组合任务的总长度,请使用{@link #setFileSize(long)}设置组合任务的长度。 * 3、由于网络或其它原因的存在,这种方式获取的组合任务大小有可能是不准确的。 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget unknownSize() { ((DGTaskWrapper) getTaskWrapper()).setUnknownSize(true); return this; @@ -89,7 +84,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * * @return 子任务管理器 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public SubTaskManager getSubTaskManager() { return mConfigHandler.getSubTaskManager(); } @@ -99,7 +93,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * * @deprecated {@link #setSubFileName(List)} 请使用该api */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) @Deprecated public GroupBuilderTarget setSubTaskFileName(List subTaskFileName) { return setSubFileName(subTaskFileName); } @@ -107,7 +100,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { /** * 设置任务组别名 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget setGroupAlias(String alias) { mConfigHandler.setGroupAlias(alias); return this; @@ -132,7 +124,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { * * @param dirPath 任务组保存文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget setDirPath(String dirPath) { return mConfigHandler.setDirPath(dirPath); } @@ -140,7 +131,6 @@ public class GroupBuilderTarget extends AbsBuilderTarget { /** * 设置子任务文件名,该方法必须在{@link #setDirPath(String)}之后调用,否则不生效 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupBuilderTarget setSubFileName(List subTaskFileName) { return mConfigHandler.setSubFileName(subTaskFileName); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 00b4d8f6..4d710cc6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -15,10 +15,8 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.util.List; @@ -39,7 +37,6 @@ public class GroupNormalTarget extends AbsNormalTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupNormalTarget option(HttpOption option) { if (option == null) { throw new NullPointerException("任务配置为空"); @@ -53,7 +50,6 @@ public class GroupNormalTarget extends AbsNormalTarget { * * @return 子任务管理器 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public SubTaskManager getSubTaskManager() { return mConfigHandler.getSubTaskManager(); } @@ -61,7 +57,6 @@ public class GroupNormalTarget extends AbsNormalTarget { /** * 设置任务组别名 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupNormalTarget setGroupAlias(String alias) { mConfigHandler.setGroupAlias(alias); return this; @@ -72,7 +67,6 @@ public class GroupNormalTarget extends AbsNormalTarget { * * @param urls 新的组合任务下载地址列表 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupNormalTarget updateUrls(List urls) { return mConfigHandler.updateUrls(urls); } @@ -96,7 +90,6 @@ public class GroupNormalTarget extends AbsNormalTarget { * * @param dirPath 任务组保存文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupNormalTarget modifyDirPath(String dirPath) { return mConfigHandler.setDirPath(dirPath); } @@ -104,7 +97,6 @@ public class GroupNormalTarget extends AbsNormalTarget { /** * 更新子任务文件名,该方法必须在{@link #modifyDirPath(String)}之后调用,否则不生效 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public GroupNormalTarget modifySubFileName(List subTaskFileName) { return mConfigHandler.setSubFileName(subTaskFileName); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index ae3fd203..534d98b0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -15,14 +15,11 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; import com.arialyy.aria.core.download.m3u8.M3U8VodOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; @@ -37,7 +34,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { getTaskWrapper().setNewTask(true); } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { if (m3U8VodOption == null) { throw new NullPointerException("m3u8任务设置为空"); @@ -48,7 +44,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { return this; } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget m3u8LiveOption(M3U8LiveOption m3U8LiveOption) { if (m3U8LiveOption == null) { throw new NullPointerException("m3u8任务设置为空"); @@ -61,7 +56,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget option(HttpOption option) { if (option == null) { throw new NullPointerException("任务配置为空"); @@ -77,8 +71,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { * * @param filePath 路径必须为文件路径,不能为文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpBuilderTarget setFilePath(@NonNull String filePath) { + public HttpBuilderTarget setFilePath(String filePath) { mConfigHandler.setTempFilePath(filePath); return this; } @@ -93,8 +86,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { * @deprecated 使用 {@link #ignoreFilePathOccupy()} */ @Deprecated - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public HttpBuilderTarget setFilePath(@NonNull String filePath, boolean forceDownload) { + public HttpBuilderTarget setFilePath(String filePath, boolean forceDownload) { mConfigHandler.setTempFilePath(filePath); mConfigHandler.setForceDownload(forceDownload); return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java index 8b8f1d3d..235ff97a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.AbsTarget; @@ -69,7 +68,6 @@ class HttpGroupConfigHandler extends AbsGroupConfigHan /** * 设置子任务文件名,该方法必须在{@link #setDirPath(String)}之后调用,否则不生效 */ - @CheckResult TARGET setSubFileName(List subTaskFileName) { if (subTaskFileName == null || subTaskFileName.isEmpty()) { ALog.w(TAG, "修改子任务的文件名失败:列表为null"); @@ -90,7 +88,6 @@ class HttpGroupConfigHandler extends AbsGroupConfigHan * * @param urls 新的组合任务下载地址列表 */ - @CheckResult TARGET updateUrls(List urls) { if (urls == null || urls.isEmpty()) { throw new NullPointerException("下载地址列表为空"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java index ea7800d0..53eb4b2c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpNormalTarget.java @@ -15,14 +15,12 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; import com.arialyy.aria.core.download.m3u8.M3U8VodOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -38,7 +36,6 @@ public class HttpNormalTarget extends AbsNormalTarget { getTaskWrapper().setNewTask(false); } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public M3U8NormalTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { if (m3U8VodOption == null) { throw new NullPointerException("m3u8任务设置为空"); @@ -49,12 +46,10 @@ public class HttpNormalTarget extends AbsNormalTarget { return new M3U8NormalTarget((DTaskWrapper) getTaskWrapper()); } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public M3U8NormalTarget m3u8VodOption() { return new M3U8NormalTarget((DTaskWrapper) getTaskWrapper()); } - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget m3u8LiveOption(M3U8LiveOption m3U8LiveOption) { if (m3U8LiveOption == null) { throw new NullPointerException("m3u8任务设置为空"); @@ -67,7 +62,6 @@ public class HttpNormalTarget extends AbsNormalTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget option(HttpOption option) { if (option == null) { throw new NullPointerException("任务配置为空"); @@ -81,7 +75,6 @@ public class HttpNormalTarget extends AbsNormalTarget { * 如:原文件路径 /mnt/sdcard/test.zip * 如果需要将test.zip改为game.zip,只需要重新设置文件路径为:/mnt/sdcard/game.zip */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget modifyFilePath(String filePath) { mConfigHandler.setTempFilePath(filePath); return this; @@ -97,7 +90,6 @@ public class HttpNormalTarget extends AbsNormalTarget { /** * 更新下载地址 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget updateUrl(String newUrl) { return mConfigHandler.updateUrl(newUrl); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java index 314294d4..77b0a8ea 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -15,11 +15,7 @@ */ package com.arialyy.aria.core.download.target; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.aria.core.common.AbsBuilderTarget; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.download.tcp.TcpDelegate; import com.arialyy.aria.core.wrapper.ITaskWrapper; /** @@ -51,10 +47,8 @@ public class TcpBuilderTarget extends AbsBuilderTarget { * * @param filePath 路径必须为文件路径,不能为文件夹路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) - public TcpBuilderTarget setFilePath(@NonNull String filePath) { + public TcpBuilderTarget setFilePath(String filePath) { mConfigHandler.setTempFilePath(filePath); return this; } - } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java index 3fc67aa2..3c9281cb 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsTarget.java @@ -16,11 +16,10 @@ package com.arialyy.aria.core.inf; import android.text.TextUtils; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.common.controller.BuilderController; import com.arialyy.aria.core.common.controller.NormalController; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -61,7 +60,6 @@ public abstract class AbsTarget { * * @param str 扩展数据 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public TARGET setExtendField(String str) { if (TextUtils.isEmpty(str)) return (TARGET) this; if (TextUtils.isEmpty(mEntity.getStr()) || !mEntity.getStr().equals(str)) { @@ -80,7 +78,6 @@ public abstract class AbsTarget { * BuilderController#add()} * 等操作任务的方法,那么你需要调用{@link NormalController#save()}才能将修改保存到数据库 */ - @CheckResult(suggest = "after use #create()、#stop()、#cancel()、#resume()、#save()?") public TARGET resetState() { getTaskWrapper().getEntity().setState(IEntity.STATE_WAIT); getTaskWrapper().setRefreshInfo(true); diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java index a4fb92b1..d2ca7f81 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/IReceiver.java @@ -49,5 +49,5 @@ public interface IReceiver { * * @return {@link ReceiverType} */ - @ReceiverType String getType(); + String getType(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/ReceiverType.java b/Aria/src/main/java/com/arialyy/aria/core/inf/ReceiverType.java index cf4aa2be..7bc13733 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/ReceiverType.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/ReceiverType.java @@ -15,19 +15,10 @@ */ package com.arialyy.aria.core.inf; -import androidx.annotation.StringDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * {@link AbsReceiver}类型 */ -@StringDef({ - ReceiverType.DOWNLOAD, - ReceiverType.UPLOAD, -}) -@Retention(RetentionPolicy.SOURCE) -public @interface ReceiverType { +public interface ReceiverType { String DOWNLOAD = "download"; String UPLOAD = "upload"; } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskWrapperManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TaskWrapperManager.java index 35bd1ec0..7dc92ff0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/TaskWrapperManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/TaskWrapperManager.java @@ -15,11 +15,11 @@ */ package com.arialyy.aria.core.manager; -import androidx.collection.LruCache; +import android.util.LruCache; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.util.concurrent.locks.Lock; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index e3f7467f..c8c8ec2e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -16,8 +16,6 @@ package com.arialyy.aria.core.upload; import android.text.TextUtils; -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; import com.arialyy.annotations.TaskEnum; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; @@ -68,8 +66,8 @@ public class UploadReceiver extends AbsReceiver { * * @param filePath 文件路径 */ - @CheckResult - public HttpBuilderTarget load(@NonNull String filePath) { + + public HttpBuilderTarget load(String filePath) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); CheckUtil.checkUploadPathIsEmpty(filePath); return UTargetFactory.getInstance() @@ -82,7 +80,7 @@ public class UploadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * AbsEntity#getId()}读取任务id */ - @CheckResult + public HttpNormalTarget load(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_HTTP); return UTargetFactory.getInstance() @@ -94,8 +92,8 @@ public class UploadReceiver extends AbsReceiver { * * @param filePath 文件路径 */ - @CheckResult - public FtpBuilderTarget loadFtp(@NonNull String filePath) { + + public FtpBuilderTarget loadFtp(String filePath) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); CheckUtil.checkUploadPathIsEmpty(filePath); return UTargetFactory.getInstance() @@ -108,7 +106,7 @@ public class UploadReceiver extends AbsReceiver { * @param taskId 任务id,可从{@link AbsBuilderTarget#create()}、{@link AbsBuilderTarget#add()}、{@link * AbsEntity#getId()}读取任务id */ - @CheckResult + public FtpNormalTarget loadFtp(long taskId) { ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_FTP); return UTargetFactory.getInstance() diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java index 12546fd3..e3b5f6a0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpBuilderTarget.java @@ -15,11 +15,8 @@ */ package com.arialyy.aria.core.upload.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.FtpOption; -import com.arialyy.aria.core.inf.Suggest; -import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -43,7 +40,6 @@ public class FtpBuilderTarget extends AbsBuilderTarget { * * @param tempUrl 上传路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget setUploadUrl(String tempUrl) { url = tempUrl; mConfigHandler.setTempUrl(tempUrl); @@ -64,7 +60,6 @@ public class FtpBuilderTarget extends AbsBuilderTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpBuilderTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java index d093d8ad..78111086 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/FtpNormalTarget.java @@ -15,10 +15,8 @@ */ package com.arialyy.aria.core.upload.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.FtpOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; @@ -39,7 +37,6 @@ public class FtpNormalTarget extends AbsNormalTarget { /** * 设置登陆、字符串编码、ftps等参数 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public FtpNormalTarget option(FtpOption option) { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 3e7e1fb5..68145b56 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -15,10 +15,8 @@ */ package com.arialyy.aria.core.upload.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; /** @@ -42,7 +40,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { * * @param tempUrl 上传路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget setUploadUrl(String tempUrl) { mConfigHandler.setTempUrl(tempUrl); return this; @@ -51,7 +48,6 @@ public class HttpBuilderTarget extends AbsBuilderTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpBuilderTarget option(HttpOption option) { if (option == null) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java index 66136b04..af61eb87 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpNormalTarget.java @@ -15,10 +15,8 @@ */ package com.arialyy.aria.core.upload.target; -import androidx.annotation.CheckResult; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.inf.Suggest; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; @@ -41,7 +39,6 @@ public class HttpNormalTarget extends AbsNormalTarget { * * @param tempUrl 上传路径 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget setUploadUrl(String tempUrl) { mConfigHandler.setTempUrl(tempUrl); return this; @@ -50,7 +47,6 @@ public class HttpNormalTarget extends AbsNormalTarget { /** * 设置http请求参数,header等信息 */ - @CheckResult(suggest = Suggest.TASK_CONTROLLER) public HttpNormalTarget option(HttpOption option) { if (option == null) { throw new NullPointerException("任务配置为空"); diff --git a/DEV_LOG.md b/DEV_LOG.md index e000b3e9..fb81df0b 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,7 @@ ## 开发日志 + + v_3.8 + - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 + - 修复一个在xml中使用fragment导致的内存泄漏问题 + v_3.7.10 (2019/12/3) - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 - fix bug https://github.com/AriaLyy/Aria/issues/542 diff --git a/FtpComponent/build.gradle b/FtpComponent/build.gradle index 10b1fbb7..a480ed42 100644 --- a/FtpComponent/build.gradle +++ b/FtpComponent/build.gradle @@ -25,8 +25,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" - implementation project(path: ':PublicComponent') } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java index ae78eeea..0bab6cf7 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpFISAdapter.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.ftp.upload; -import androidx.annotation.NonNull; import com.arialyy.aria.util.BufferedRandomAccessFile; import java.io.IOException; import java.io.InputStream; @@ -34,12 +33,12 @@ final class FtpFISAdapter extends InputStream { void onProgressCallback(byte[] buffer, int byteOffset, int byteCount) throws IOException; } - FtpFISAdapter(@NonNull BufferedRandomAccessFile is, @NonNull ProgressCallback callback) { + FtpFISAdapter(BufferedRandomAccessFile is, ProgressCallback callback) { mIs = is; mCallback = callback; } - FtpFISAdapter(@NonNull BufferedRandomAccessFile is) { + FtpFISAdapter(BufferedRandomAccessFile is) { mIs = is; } @@ -51,7 +50,7 @@ final class FtpFISAdapter extends InputStream { return mIs.read(); } - @Override public int read(@NonNull byte[] buffer) throws IOException { + @Override public int read(byte[] buffer) throws IOException { count = mIs.read(buffer); if (mCallback != null) { mCallback.onProgressCallback(buffer, 0, count); @@ -59,7 +58,7 @@ final class FtpFISAdapter extends InputStream { return count; } - @Override public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) + @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { count = mIs.read(buffer, byteOffset, byteCount); if (mCallback != null) { diff --git a/HttpComponent/build.gradle b/HttpComponent/build.gradle index 287ed5e8..f16fd533 100644 --- a/HttpComponent/build.gradle +++ b/HttpComponent/build.gradle @@ -10,7 +10,6 @@ android { versionCode rootProject.ext.versionCode versionName rootProject.ext.versionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' } @@ -25,9 +24,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" - - testImplementation 'junit:junit:4.12' implementation project(path: ':PublicComponent') } diff --git a/M3U8Component/build.gradle b/M3U8Component/build.gradle index d44a132e..6c08e9ef 100644 --- a/M3U8Component/build.gradle +++ b/M3U8Component/build.gradle @@ -25,7 +25,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" implementation project(path: ':HttpComponent') implementation project(path: ':PublicComponent') } diff --git a/PublicComponent/build.gradle b/PublicComponent/build.gradle index 50ccd8df..8df6b54e 100644 --- a/PublicComponent/build.gradle +++ b/PublicComponent/build.gradle @@ -24,7 +24,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" testImplementation 'junit:junit:4.12' } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java index 24b8ede4..7a238f39 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/FtpUrlEntity.java @@ -92,7 +92,6 @@ public class FtpUrlEntity implements Cloneable { * 连接协议 * {@link ProtocolType} */ - @ProtocolType public String protocol = ProtocolType.Default; /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java index d1602bd0..17ef1d7a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/ProtocolType.java @@ -15,20 +15,7 @@ */ package com.arialyy.aria.core; -import androidx.annotation.StringDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@StringDef({ - ProtocolType.Default, - ProtocolType.SSL, - ProtocolType.SSLv3, - ProtocolType.TLS, - ProtocolType.TLSv1, - ProtocolType.TLSv1_1, - ProtocolType.TLSv1_2 -}) -@Retention(RetentionPolicy.SOURCE) public @interface ProtocolType { +public interface ProtocolType { String Default = "TLS"; String SSL = "SSL"; String SSLv3 = "SSLv3"; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java index 357c485d..7a8d8113 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/ConfigType.java @@ -15,17 +15,7 @@ */ package com.arialyy.aria.core.config; -import androidx.annotation.IntDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@IntDef({ - ConfigType.DOWNLOAD, - ConfigType.UPLOAD, - ConfigType.APP, - ConfigType.D_GROUP -}) -@Retention(RetentionPolicy.SOURCE) @interface ConfigType { +public interface ConfigType { int DOWNLOAD = 1; int UPLOAD = 2; int APP = 3; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java index d3472130..4b17fea4 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java @@ -33,7 +33,7 @@ public class XMLReader extends DefaultHandler { private UploadConfig mUploadConfig = Configuration.getInstance().uploadCfg; private AppConfig mAppConfig = Configuration.getInstance().appCfg; private DGroupConfig mDGroupConfig = Configuration.getInstance().dGroupCfg; - private @ConfigType int mType; + private int mType; @Override public void startDocument() throws SAXException { super.startDocument(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java index 77e1dfbf..a521cbc2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/TaskSchedulerType.java @@ -1,17 +1,6 @@ package com.arialyy.aria.core.inf; -import androidx.annotation.IntDef; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@IntDef({ - TaskSchedulerType.TYPE_DEFAULT, - TaskSchedulerType.TYPE_STOP_NOT_NEXT, - TaskSchedulerType.TYPE_STOP_AND_WAIT, - TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY, - TaskSchedulerType.TYPE_START_AND_RESET_STATE -}) -@Retention(RetentionPolicy.SOURCE) public @interface TaskSchedulerType { +public interface TaskSchedulerType { int TYPE_DEFAULT = 1; /** * 停止当前任务并且不自动启动下一任务 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java index 5f093806..7810aa41 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/processor/ITsMergeHandler.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.core.processor; -import androidx.annotation.Nullable; import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.inf.IEventHandler; import java.util.List; @@ -33,5 +32,5 @@ public interface ITsMergeHandler extends IEventHandler { * @param tsPath ts文件列表 * @return {@code true} 合并成功 */ - boolean merge(@Nullable M3U8Entity m3U8Entity, List tsPath); + boolean merge(M3U8Entity m3U8Entity, List tsPath); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java index 514129a6..503c0869 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java @@ -52,7 +52,6 @@ public abstract class AbsTask /** * 该任务的调度类型 */ - @TaskSchedulerType private int mSchedulerType = TaskSchedulerType.TYPE_DEFAULT; protected IEventListener mListener; @@ -193,7 +192,7 @@ public abstract class AbsTask start(TaskSchedulerType.TYPE_DEFAULT); } - @Override public void start(@TaskSchedulerType int type) { + @Override public void start(int type) { mSchedulerType = type; if (type == TaskSchedulerType.TYPE_START_AND_RESET_STATE) { if (getUtil().isRunning()) { @@ -216,7 +215,7 @@ public abstract class AbsTask stop(TaskSchedulerType.TYPE_DEFAULT); } - @Override public void stop(@TaskSchedulerType int type) { + @Override public void stop(int type) { isStop = true; mSchedulerType = type; getUtil().stop(); @@ -226,7 +225,7 @@ public abstract class AbsTask cancel(TaskSchedulerType.TYPE_DEFAULT); } - @Override public void cancel(@TaskSchedulerType int type) { + @Override public void cancel(int type) { isCancel = true; mSchedulerType = type; getUtil().cancel(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java index e9f1e5a4..bb92458a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITask.java @@ -91,7 +91,7 @@ public interface ITask { * * @param type {@link TaskSchedulerType} */ - void start(@TaskSchedulerType int type); + void start(int type); /** * 停止任务 @@ -103,7 +103,7 @@ public interface ITask { * * @param type {@link TaskSchedulerType} */ - void stop(@TaskSchedulerType int type); + void stop(int type); /** * 删除任务 @@ -115,7 +115,7 @@ public interface ITask { * * @param type {@link TaskSchedulerType} */ - void cancel(@TaskSchedulerType int type); + void cancel(int type); /** * 读取扩展数据 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java index 1b9b6016..dd2b14a1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskObserver.java @@ -16,7 +16,6 @@ package com.arialyy.aria.core.task; import android.os.Bundle; -import androidx.annotation.Nullable; import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.exception.BaseException; @@ -33,7 +32,7 @@ public interface IThreadTaskObserver { * * @param state state {@link IThreadState#STATE_STOP}.. */ - void updateState(int state, @Nullable Bundle bundle); + void updateState(int state, Bundle bundle); /** * 更新完成的状态 @@ -45,7 +44,7 @@ public interface IThreadTaskObserver { * * @param needRetry 是否需要重试,一般是网络错误才需要重试 */ - void updateFailState(@Nullable BaseException e, boolean needRetry); + void updateFailState(BaseException e, boolean needRetry); /** * 更新进度 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 568e2b43..d4ea3464 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -20,7 +20,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Process; -import androidx.annotation.Nullable; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.SubThreadConfig; @@ -259,7 +258,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * @param bundle 而外数据 */ @Override - public synchronized void updateState(int state, @Nullable Bundle bundle) { + public synchronized void updateState(int state, Bundle bundle) { Message msg = mStateHandler.obtainMessage(); msg.what = state; if (state != IThreadState.STATE_UPDATE_PROGRESS) { @@ -297,7 +296,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * * @param needRetry 是否需要重试,一般是网络错误才需要重试 */ - @Override public synchronized void updateFailState(@Nullable BaseException e, boolean needRetry) { + @Override public synchronized void updateFailState(BaseException e, boolean needRetry) { fail(mRangeProgress, e, needRetry); } @@ -448,7 +447,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { /** * 发送失败信息 */ - private void sendFailMsg(@Nullable BaseException e,boolean needRetry) { + private void sendFailMsg(BaseException e, boolean needRetry) { Bundle b = new Bundle(); b.putBoolean(IThreadState.KEY_RETRY, needRetry); if (e != null) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java index 78395132..0271104f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/SSLContextUtil.java @@ -56,8 +56,7 @@ public class SSLContextUtil { * @param caPath 保存在assets目录下的CA证书完整路径 * @param protocol 连接协议 */ - public static SSLContext getSSLContextFromAssets(String caAlias, String caPath, - @ProtocolType String protocol) { + public static SSLContext getSSLContextFromAssets(String caAlias, String caPath, String protocol) { if (TextUtils.isEmpty(caAlias) || TextUtils.isEmpty(caPath)) { return null; } @@ -83,8 +82,7 @@ public class SSLContextUtil { * @param caPath CA证书路径 * @param protocol 连接协议 */ - public static SSLContext getSSLContext(String caAlias, String caPath, - @ProtocolType String protocol) { + public static SSLContext getSSLContext(String caAlias, String caPath, String protocol) { if (TextUtils.isEmpty(caAlias) || TextUtils.isEmpty(caPath)) { return null; } @@ -105,8 +103,8 @@ public class SSLContextUtil { /** * @param cacheKey 别名 + 证书路径,然后取md5 */ - private static SSLContext createContext(String caAlias, Certificate ca, - @ProtocolType String protocol, String cacheKey) { + private static SSLContext createContext(String caAlias, Certificate ca, String protocol, + String cacheKey) { try { String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); @@ -154,7 +152,7 @@ public class SSLContextUtil { /** * 服务器证书不是由 CA 签署的,而是自签署时,获取默认的SSL */ - public static SSLContext getDefaultSLLContext(@ProtocolType String protocol) { + public static SSLContext getDefaultSLLContext(String protocol) { SSLContext sslContext = null; try { sslContext = diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java index 7f39f5d7..cc14d039 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/WeakHandler.java @@ -27,9 +27,6 @@ package com.arialyy.aria.util; import android.os.Handler; import android.os.Looper; import android.os.Message; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import java.lang.ref.WeakReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -51,7 +48,7 @@ import java.util.concurrent.locks.ReentrantLock; // hard reference to Callback. We need to keep callback in memory private final ExecHandler mExec; private Lock mLock = new ReentrantLock(); - @SuppressWarnings("ConstantConditions") @VisibleForTesting final ChainedRef mRunnables = + @SuppressWarnings("ConstantConditions") final ChainedRef mRunnables = new ChainedRef(mLock, null); /** @@ -76,7 +73,7 @@ import java.util.concurrent.locks.ReentrantLock; * * @param callback The callback interface in which to handle messages, or null. */ - public WeakHandler(@Nullable Handler.Callback callback) { + public WeakHandler(Handler.Callback callback) { mCallback = callback; // Hard referencing body mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler } @@ -86,7 +83,7 @@ import java.util.concurrent.locks.ReentrantLock; * * @param looper The looper, must not be null. */ - public WeakHandler(@NonNull Looper looper) { + public WeakHandler(Looper looper) { mCallback = null; mExec = new ExecHandler(looper); } @@ -98,7 +95,7 @@ import java.util.concurrent.locks.ReentrantLock; * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. */ - public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { + public WeakHandler(Looper looper, Handler.Callback callback) { mCallback = callback; mExec = new ExecHandler(looper, new WeakReference<>(callback)); } @@ -113,7 +110,7 @@ import java.util.concurrent.locks.ReentrantLock; * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ - public final boolean post(@NonNull Runnable r) { + public final boolean post(Runnable r) { return mExec.post(wrapRunnable(r)); } @@ -133,7 +130,7 @@ import java.util.concurrent.locks.ReentrantLock; * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ - public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { + public final boolean postAtTime(Runnable r, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), uptimeMillis); } @@ -367,7 +364,7 @@ import java.util.concurrent.locks.ReentrantLock; return mExec.getLooper(); } - private WeakRunnable wrapRunnable(@NonNull Runnable r) { + private WeakRunnable wrapRunnable(Runnable r) { //noinspection ConstantConditions if (r == null) { throw new NullPointerException("Runnable can't be null"); @@ -398,7 +395,7 @@ import java.util.concurrent.locks.ReentrantLock; mCallback = callback; } - @Override public void handleMessage(@NonNull Message msg) { + @Override public void handleMessage(Message msg) { if (mCallback == null) { return; } @@ -432,14 +429,14 @@ import java.util.concurrent.locks.ReentrantLock; } static class ChainedRef { - @Nullable ChainedRef next; - @Nullable ChainedRef prev; - @NonNull final Runnable runnable; - @NonNull final WeakRunnable wrapper; + ChainedRef next; + ChainedRef prev; + final Runnable runnable; + final WeakRunnable wrapper; - @NonNull Lock lock; + Lock lock; - public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { + public ChainedRef(Lock lock, Runnable r) { this.runnable = r; this.lock = lock; this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); @@ -462,7 +459,7 @@ import java.util.concurrent.locks.ReentrantLock; return wrapper; } - public void insertAfter(@NonNull ChainedRef candidate) { + public void insertAfter(ChainedRef candidate) { lock.lock(); try { if (this.next != null) { @@ -477,7 +474,7 @@ import java.util.concurrent.locks.ReentrantLock; } } - @Nullable public WeakRunnable remove(Runnable obj) { + public WeakRunnable remove(Runnable obj) { lock.lock(); try { ChainedRef curr = this.next; // Skipping head diff --git a/SFtpComponent/build.gradle b/SFtpComponent/build.gradle index 9aa5c8cd..3e7e3a3f 100644 --- a/SFtpComponent/build.gradle +++ b/SFtpComponent/build.gradle @@ -25,7 +25,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" implementation "com.jcraft:jsch:0.1.55" implementation "com.jcraft:jzlib:1.1.3" implementation project(path: ':FtpComponent') diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index 5f5aa6b5..59365c1b 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -35,7 +35,7 @@ public class UploadModule extends BaseViewModule { * 获取Ftp上传信息 */ LiveData getFtpInfo(Context context) { - String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aa/你好"); + String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aab/你好"); String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db"); From f3d5e0135aa131a6119656a92027ee0a4b9194e5 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sun, 15 Dec 2019 21:52:50 +0800 Subject: [PATCH 035/140] =?UTF-8?q?m3u8=E5=A2=9E=E5=8A=A0=E4=BA=86`ignoreF?= =?UTF-8?q?ailureTs`=E6=96=B9=E6=B3=95=EF=BC=8C=E5=BF=BD=E7=95=A5=E8=99=BE?= =?UTF-8?q?=E7=B1=BB=E5=A4=B1=E8=B4=A5=E7=9A=84ts=E5=88=87=E7=89=87=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=9C=A8dialogFragment=E7=9A=84`onCreateDial?= =?UTF-8?q?og()`=E6=B3=A8=E5=86=8C=E5=AF=BC=E8=87=B4=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arialyy/aria/core/WidgetLiftManager.java | 8 +++ .../arialyy/aria/core/common/ProxyHelper.java | 63 ++++++++----------- .../aria/core/download/m3u8/M3U8Option.java | 9 +++ DEV_LOG.md | 3 + .../com/arialyy/aria/m3u8/M3U8InfoThread.java | 4 ++ .../com/arialyy/aria/m3u8/M3U8TaskOption.java | 14 +++++ .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 14 +++-- .../aria/core/download/M3U8Entity.java | 28 +++++++++ .../core/download/KotlinDownloadActivity.kt | 27 +++++--- build.gradle | 2 +- 10 files changed, 119 insertions(+), 53 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java b/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java index f19b5ab6..1ab43e76 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/WidgetLiftManager.java @@ -73,8 +73,16 @@ final class WidgetLiftManager { /** * 处理对话框取消或dismiss + * + * @return true 设置了dialog的销毁事件。false 没有设置dialog的销毁事件 */ boolean handleDialogLift(Dialog dialog) { + if (dialog == null) { + ALog.w(TAG, + "dialog 为空,没有设置自动销毁事件,为了防止内存泄露,请在dismiss方法中调用Aria.download(this).unRegister();来注销事件\n" + + "如果你使用的是DialogFragment,那么你需要在onDestroy()中进行销毁Aria事件操作"); + return false; + } try { Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage"); Message dismissMsg = (Message) dismissField.get(dialog); diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/ProxyHelper.java b/Aria/src/main/java/com/arialyy/aria/core/common/ProxyHelper.java index 780f262b..ff9c0ea8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/ProxyHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/ProxyHelper.java @@ -75,50 +75,23 @@ public class ProxyHelper { return result; } result = new HashSet<>(); - try { - if (getClass().getClassLoader() - .loadClass(className.concat(TaskEnum.DOWNLOAD_GROUP.proxySuffix)) - != null) { - result.add(PROXY_TYPE_DOWNLOAD_GROUP); - } - } catch (ClassNotFoundException e) { - //e.printStackTrace(); + if (checkProxyExist(className, TaskEnum.DOWNLOAD_GROUP.proxySuffix)) { + result.add(PROXY_TYPE_DOWNLOAD_GROUP); } - try { - if (getClass().getClassLoader().loadClass(className.concat(TaskEnum.DOWNLOAD.proxySuffix)) - != null) { - result.add(PROXY_TYPE_DOWNLOAD); - } - } catch (ClassNotFoundException e) { - //e.printStackTrace(); + if (checkProxyExist(className, TaskEnum.DOWNLOAD.proxySuffix)) { + result.add(PROXY_TYPE_DOWNLOAD); } - try { - if (getClass().getClassLoader().loadClass(className.concat(TaskEnum.UPLOAD.proxySuffix)) - != null) { - result.add(PROXY_TYPE_UPLOAD); - } - } catch (ClassNotFoundException e) { - //e.printStackTrace(); + if (checkProxyExist(className, TaskEnum.UPLOAD.proxySuffix)) { + result.add(PROXY_TYPE_UPLOAD); } - try { - if (getClass().getClassLoader().loadClass(className.concat(TaskEnum.M3U8_PEER.proxySuffix)) - != null) { - result.add(PROXY_TYPE_M3U8_PEER); - } - } catch (ClassNotFoundException e) { - //e.printStackTrace(); + if (checkProxyExist(className, TaskEnum.M3U8_PEER.proxySuffix)) { + result.add(PROXY_TYPE_M3U8_PEER); } - try { - if (getClass().getClassLoader() - .loadClass(className.concat(TaskEnum.DOWNLOAD_GROUP_SUB.proxySuffix)) - != null) { - result.add(PROXY_TYPE_DOWNLOAD_GROUP_SUB); - } - } catch (ClassNotFoundException e) { - //e.printStackTrace(); + if (checkProxyExist(className, TaskEnum.DOWNLOAD_GROUP_SUB.proxySuffix)) { + result.add(PROXY_TYPE_DOWNLOAD_GROUP_SUB); } if (!result.isEmpty()) { @@ -126,4 +99,20 @@ public class ProxyHelper { } return result; } + + private boolean checkProxyExist(String className, String proxySuffix) { + String clsName = className.concat(proxySuffix); + + try { + if (getClass().getClassLoader().loadClass(clsName) != null) { + return true; + } + if (Class.forName(clsName) != null) { + return true; + } + } catch (ClassNotFoundException e) { + //e.printStackTrace(); + } + return false; + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java index eb921c8a..77e329a5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -33,12 +33,21 @@ public class M3U8Option extends BaseOption { private ITsMergeHandler mergeHandler; private IBandWidthUrlConverter bandWidthUrlConverter; private IKeyUrlConverter keyUrlConverter; + private boolean ignoreFailureTs = false; M3U8Option() { super(); ComponentUtil.getInstance().checkComponentExist(ComponentUtil.COMPONENT_TYPE_M3U8); } + /** + * 忽略下载失败的ts切片,即使有失败的切片,下载完成后也要合并所有切片,并进入complete回调 + */ + public OP ignoreFailureTs() { + this.ignoreFailureTs = true; + return (OP) this; + } + /** * 生成m3u8索引文件 * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 diff --git a/DEV_LOG.md b/DEV_LOG.md index fb81df0b..7b92d358 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -2,6 +2,9 @@ + v_3.8 - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 - 修复一个在xml中使用fragment导致的内存泄漏问题 + - m3u8协议的key信息增加了`keyFormat`,`keyFormatVersion`字段 + - m3u8增加了`ignoreFailureTs`方法,忽略虾类失败的ts切片 + - 修复在dialogFragment的`onCreateDialog()`注册导致的注解不生效问题 + v_3.7.10 (2019/12/3) - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 - fix bug https://github.com/AriaLyy/Aria/issues/542 diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java index 4c92af17..7d3ef2d4 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java @@ -255,6 +255,10 @@ final public class M3U8InfoThread implements Runnable { m3U8Entity.keyUrl) + ".key"; } else if (param.startsWith("IV")) { m3U8Entity.iv = param.split("=")[1]; + }else if (param.startsWith("KEYFORMAT")){ + m3U8Entity.keyFormat = param.split("=")[1]; + }else if (param.startsWith("KEYFORMATVERSIONS")){ + m3U8Entity.keyFormatVersion = param.split("=")[1]; } } downloadKey(m3U8Entity); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index 5e9df9fe..b6c35052 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -109,6 +109,20 @@ public class M3U8TaskOption implements ITaskOption { */ private SoftReference keyUrlConverter; + /** + * 忽略下载失败的ts切片。 + * true:即使有失败的切片,下载完成后也要合并所有切片,并进入complete回调 + */ + private boolean ignoreFailureTs = false; + + public boolean isIgnoreFailureTs() { + return ignoreFailureTs; + } + + public void setIgnoreFailureTs(boolean ignoreFailureTs) { + this.ignoreFailureTs = ignoreFailureTs; + } + public IKeyUrlConverter getKeyUrlConverter() { return keyUrlConverter == null ? null : keyUrlConverter.get(); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 86a2ceb5..49024b45 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -221,9 +221,9 @@ public class M3U8VodLoader extends BaseM3U8Loader { } } mManager.updateStateCount(); - if (mCompleteNum <= 0){ + if (mCompleteNum <= 0) { mListener.onStart(0); - }else { + } else { int percent = mCompleteNum * 100 / mRecord.threadRecords.size(); mListener.onResume(percent); } @@ -570,9 +570,9 @@ public class M3U8VodLoader extends BaseM3U8Loader { ALog.d(TAG, String.format("vod任务【%s】完成", mTempFile.getName())); if (mM3U8Option.isGenerateIndexFile()) { - if (generateIndexFile(false)){ + if (generateIndexFile(false)) { listener.onComplete(); - }else { + } else { listener.onFail(false, new TaskException(TAG, "创建索引文件失败")); } } else if (mM3U8Option.isMergeFile()) { @@ -621,7 +621,11 @@ public class M3U8VodLoader extends BaseM3U8Loader { } @Override public boolean isComplete() { - return mCompleteNum == taskRecord.threadRecords.size() && !isJump; + if (mM3U8Option.isIgnoreFailureTs()) { + return mCompleteNum + failNum >= taskRecord.threadRecords.size() && !isJump; + } else { + return mCompleteNum == taskRecord.threadRecords.size() && !isJump; + } } @Override public long getCurrentProgress() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java index 2a04816f..d024cb43 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java @@ -21,6 +21,7 @@ import android.text.TextUtils; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.orm.annotation.Default; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.DbDataHelper; import java.io.File; @@ -78,6 +79,33 @@ public class M3U8Entity extends DbEntity implements Parcelable { */ public String iv; + /** + * key的格式,可能为空 + */ + public String keyFormat; + + /** + * key的格式版本,默认为1,如果是多个版本,使用"/"分隔,如:"1", "1/2", or "1/2/5" + */ + @Default("1") + public String keyFormatVersion = "1"; + + public String getKeyFormat() { + return keyFormat; + } + + public void setKeyFormat(String keyFormat) { + this.keyFormat = keyFormat; + } + + public String getKeyFormatVersion() { + return keyFormatVersion; + } + + public void setKeyFormatVersion(String keyFormatVersion) { + this.keyFormatVersion = keyFormatVersion; + } + public String getKeyPath() { return keyPath; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt index 495a39c0..0afb5956 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt +++ b/app/src/main/java/com/arialyy/simple/core/download/KotlinDownloadActivity.kt @@ -50,7 +50,7 @@ class KotlinDownloadActivity : BaseActivity() { private var mUrl: String? = null private var mFilePath: String? = null private var mModule: HttpDownloadModule? = null - private val mTaskId: Long = -1 + private var mTaskId: Long = -1 internal var receiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive( @@ -104,7 +104,7 @@ class KotlinDownloadActivity : BaseActivity() { .get(HttpDownloadModule::class.java) mModule!!.getHttpDownloadInfo(this) .observe(this, Observer { entity -> - if (entity == null || entity.id < 0) { + if (entity == null) { return@Observer } if (entity.state == IEntity.STATE_STOP) { @@ -286,14 +286,21 @@ class KotlinDownloadActivity : BaseActivity() { } private fun startD() { - Aria.download(this) - .load(mUrl!!) - //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") - //.addHeader("Accept-Encoding", "gzip, deflate") - //.addHeader("DNT", "1") - //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") - .setFilePath(mFilePath!!, true) - .create() + if (mTaskId == -1L) { + + mTaskId = Aria.download(this) + .load(mUrl!!) + //.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3") + //.addHeader("Accept-Encoding", "gzip, deflate") + //.addHeader("DNT", "1") + //.addHeader("Cookie", "BAIDUID=648E5FF020CC69E8DD6F492D1068AAA9:FG=1; BIDUPSID=648E5FF020CC69E8DD6F492D1068AAA9; PSTM=1519099573; BD_UPN=12314753; locale=zh; BDSVRTM=0") + .setFilePath(mFilePath!!, true) + .create() + } else { + Aria.download(this) + .load(mTaskId) + .resume() + } } override fun onStop() { diff --git a/build.gradle b/build.gradle index 72db2aac..42116f03 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ task clean(type: Delete) { ext { versionCode = 380 - versionName = '3.7.10' + versionName = '3.8_pre_1' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 0d93953cd918801f8b18ed10f8b0514f98580abc Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 16 Dec 2019 20:18:45 +0800 Subject: [PATCH 036/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BB=84=E5=90=88?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=88=9D=E5=A7=8B=E5=8C=96=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=97=A0=E6=B3=95=E5=88=A0=E9=99=A4=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=E4=BF=AE=E5=A4=8D`reStart()`=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E6=97=A0=E6=B3=95=E5=81=9C=E6=AD=A2=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20ftp=E5=A2=9E=E5=8A=A0=E4=B8=BB=E5=8A=A8=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=BC=80=E5=90=AF=E4=B8=BB=E5=8A=A8=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=9Ahttps://aria.laoyuyu.me/aria=5Fdoc/api/ftp=5F?= =?UTF-8?q?params.html=20=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arialyy/frame/util/RegularExpression.java | 3 +- .../arialyy/aria/core/command/ReStartCmd.java | 6 +- .../aria/core/common/AbsNormalTarget.java | 4 +- .../arialyy/aria/core/common/FtpOption.java | 60 ++++++ .../common/controller/BuilderController.java | 7 +- .../common/controller/FeatureController.java | 24 ++- .../common/controller/INormalFeature.java | 2 +- .../common/controller/NormalController.java | 16 +- .../aria/core/download/CheckDEntityUtil.java | 18 +- .../aria/core/download/CheckDGEntityUtil.java | 13 +- .../core/download/CheckFtpDirEntityUtil.java | 9 +- .../arialyy/aria/core/queue/AbsTaskQueue.java | 20 +- .../arialyy/aria/core/queue/ITaskQueue.java | 21 ++- .../aria/core/upload/CheckUEntityUtil.java | 8 +- DEV_LOG.md | 4 + .../apache/commons/net/ftp/FTPClient.java | 23 ++- .../arialyy/aria/ftp/AbsFtpInfoThread.java | 172 +++++++++--------- .../aria/ftp/BaseFtpThreadTaskAdapter.java | 13 +- .../arialyy/aria/ftp/FtpDirInfoThread.java | 9 +- .../com/arialyy/aria/ftp/FtpTaskOption.java | 51 +++++- .../arialyy/aria/http/HttpFileInfoThread.java | 4 +- .../arialyy/aria/core/common/ErrorCode.java | 14 +- .../aria/core/common/FtpConnectionMode.java | 30 +++ .../aria/core/common/RecordHandler.java | 1 + .../java/com/arialyy/aria/util/CheckUtil.java | 41 +++++ .../core/download/FtpDownloadActivity.java | 12 +- .../core/download/FtpDownloadModule.java | 3 +- .../core/download/HttpDownloadModule.java | 5 +- .../core/download/SingleTaskActivity.java | 12 +- .../core/download/mutil/DownloadAdapter.java | 2 +- .../core/download/mutil/FileListAdapter.java | 2 + .../main/res/layout/activity_ftp_download.xml | 10 +- app/src/main/res/layout/activity_single.xml | 6 - 33 files changed, 437 insertions(+), 188 deletions(-) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/FtpConnectionMode.java diff --git a/AppFrame/src/main/java/com/arialyy/frame/util/RegularExpression.java b/AppFrame/src/main/java/com/arialyy/frame/util/RegularExpression.java index 4b405f48..7b05cac8 100644 --- a/AppFrame/src/main/java/com/arialyy/frame/util/RegularExpression.java +++ b/AppFrame/src/main/java/com/arialyy/frame/util/RegularExpression.java @@ -92,5 +92,6 @@ public class RegularExpression { /** * IP */ - public static String IP = "\\d+\\.\\d+\\.\\d+\\.\\d+"; + public static String IP = + "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java index 10598993..a2bef76c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/ReStartCmd.java @@ -15,9 +15,9 @@ */ package com.arialyy.aria.core.command; +import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.TaskSchedulerType; /** * 重新开始任务命令 @@ -34,8 +34,8 @@ final class ReStartCmd extends AbsNormalCmd { task = createTask(); } if (task != null) { - task.cancel(TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY); - task.start(TaskSchedulerType.TYPE_START_AND_RESET_STATE); + mQueue.cancelTask(task, TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY); + mQueue.startTask(task, TaskSchedulerType.TYPE_START_AND_RESET_STATE); } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index 71a6c789..4920b9a6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -202,8 +202,8 @@ public abstract class AbsNormalTarget extends Ab * 重新下载 */ @Override - public void reStart() { - getController().reStart(); + public long reStart() { + return getController().reStart(); } @Override diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java index 168f195e..c162cd83 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java @@ -33,6 +33,9 @@ public class FtpOption extends BaseOption { private String protocol, keyAlias, storePass, storePath; private boolean isImplicit = true; private IFtpUploadInterceptor uploadInterceptor; + private int connMode = FtpConnectionMode.DATA_CONNECTION_MODE_PASV; + private int minPort, maxPort; + private String activeExternalIPAddress; public FtpOption() { super(); @@ -143,6 +146,63 @@ public class FtpOption extends BaseOption { return this; } + /** + * 设置数据传输模式,默认为被动模式。 + * 主动模式:传输文件时,客户端开启一个端口,ftp服务器连接到客户端的该端口,ftp服务器推送数据到客户端 + * 被动模式:传输文件时,ftp服务器开启一个端口,客户端连接到ftp服务器的这个端口,客户端请求ftp服务器的数据 + * 请注意:主动模式是服务器主动连接到android,如果使用住的模式,请确保ftp服务器能ping通android + * + * @param connMode {@link FtpConnectionMode#DATA_CONNECTION_MODE_PASV}, + * {@link FtpConnectionMode#DATA_CONNECTION_MODE_ACTIVITY} + */ + public FtpOption setConnectionMode(int connMode) { + if (connMode != FtpConnectionMode.DATA_CONNECTION_MODE_PASV + && connMode != FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) { + ALog.e(TAG, "连接模式设置失败,默认启用被动模式"); + return this; + } + this.connMode = connMode; + return this; + } + + /** + * 主动模式下的端口范围 + */ + public FtpOption setActivePortRange(int minPort, int maxPort) { + + if (minPort > maxPort) { + ALog.e(TAG, "设置端口范围错误,minPort > maxPort"); + return this; + } + if (minPort <= 0 || minPort >= 65535) { + ALog.e(TAG, "端口范围错误"); + return this; + } + if (maxPort >= 65535) { + ALog.e(TAG, "端口范围错误"); + return this; + } + this.minPort = minPort; + this.maxPort = maxPort; + return this; + } + + /** + * 主动模式下,对外ip(可被Ftp服务器访问的ip) + */ + public FtpOption setActiveExternalIPAddress(String ip) { + if (TextUtils.isEmpty(ip)) { + ALog.e(TAG, "ip为空"); + return this; + } + if (!CheckUtil.checkIp(ip)) { + ALog.e(TAG, "ip地址错误:" + ip); + return this; + } + this.activeExternalIPAddress = ip; + return this; + } + public void setUrlEntity(FtpUrlEntity urlEntity) { this.urlEntity = urlEntity; urlEntity.needLogin = isNeedLogin; diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java index d66b25a1..70002c01 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/BuilderController.java @@ -15,11 +15,10 @@ */ package com.arialyy.aria.core.common.controller; +import com.arialyy.aria.core.command.CmdHelper; import com.arialyy.aria.core.command.NormalCmdFactory; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.command.CmdHelper; -import com.arialyy.aria.util.ALog; /** * 创建任务时使用的控制器 @@ -36,6 +35,7 @@ public final class BuilderController extends FeatureController implements IStart * @return 正常添加,返回任务id,否则返回-1 */ public long add() { + setAction(ACTION_ADD); if (checkConfig()) { EventMsgUtil.getDefault() .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CREATE, @@ -51,17 +51,18 @@ public final class BuilderController extends FeatureController implements IStart * @return 正常启动,返回任务id,否则返回-1 */ public long create() { + setAction(ACTION_CREATE); if (checkConfig()) { EventMsgUtil.getDefault() .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, checkTaskType())); return getEntity().getId(); } - return -1; } @Override public long setHighestPriority() { + setAction(ACTION_PRIORITY); if (checkConfig()) { EventMsgUtil.getDefault() .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_HIGHEST_PRIORITY, diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java index 875d2ad4..92322853 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/FeatureController.java @@ -43,6 +43,17 @@ import java.lang.reflect.InvocationTargetException; * 功能控制器 */ public abstract class FeatureController { + private static final int ACTION_DEF = 0; + public static final int ACTION_CREATE = 1; + public static final int ACTION_RESUME = 2; + public static final int ACTION_STOP = 3; + public static final int ACTION_CANCEL = 4; + public static final int ACTION_ADD = 5; + public static final int ACTION_PRIORITY = 6; + public static final int ACTION_RETRY = 7; + public static final int ACTION_RESTART = 8; + public static final int ACTION_SAVE = 9; + private final String TAG; private AbsTaskWrapper mTaskWrapper; @@ -50,6 +61,7 @@ public abstract class FeatureController { * 是否忽略权限检查 true 忽略权限检查 */ private boolean ignoreCheckPermissions = false; + private int action = ACTION_DEF; FeatureController(AbsTaskWrapper wrapper) { mTaskWrapper = wrapper; @@ -91,6 +103,10 @@ public abstract class FeatureController { return null; } + void setAction(int action) { + this.action = action; + } + /** * 是否忽略权限检查 */ @@ -180,15 +196,15 @@ public abstract class FeatureController { private boolean checkEntity() { ICheckEntityUtil checkUtil = null; if (mTaskWrapper instanceof DTaskWrapper) { - checkUtil = CheckDEntityUtil.newInstance((DTaskWrapper) mTaskWrapper); + checkUtil = CheckDEntityUtil.newInstance((DTaskWrapper) mTaskWrapper, action); } else if (mTaskWrapper instanceof DGTaskWrapper) { if (mTaskWrapper.getRequestType() == ITaskWrapper.D_FTP_DIR) { - checkUtil = CheckFtpDirEntityUtil.newInstance((DGTaskWrapper) mTaskWrapper); + checkUtil = CheckFtpDirEntityUtil.newInstance((DGTaskWrapper) mTaskWrapper, action); } else if (mTaskWrapper.getRequestType() == ITaskWrapper.DG_HTTP) { - checkUtil = CheckDGEntityUtil.newInstance((DGTaskWrapper) mTaskWrapper); + checkUtil = CheckDGEntityUtil.newInstance((DGTaskWrapper) mTaskWrapper, action); } } else if (mTaskWrapper instanceof UTaskWrapper) { - checkUtil = CheckUEntityUtil.newInstance((UTaskWrapper) mTaskWrapper); + checkUtil = CheckUEntityUtil.newInstance((UTaskWrapper) mTaskWrapper, action); } return checkUtil != null && checkUtil.checkEntity(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java index 968098fd..3808e1ba 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/INormalFeature.java @@ -59,7 +59,7 @@ public interface INormalFeature { /** * 重新下载 */ - void reStart(); + long reStart(); /** * 保存数据 diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java index 8d019d01..f1c18a22 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/controller/NormalController.java @@ -38,6 +38,7 @@ public final class NormalController extends FeatureController implements INormal */ @Override public void stop() { + setAction(ACTION_STOP); if (checkConfig()) { EventMsgUtil.getDefault() .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_STOP, @@ -60,6 +61,7 @@ public final class NormalController extends FeatureController implements INormal * @param newStart true 立即将任务恢复到执行队列中 */ @Override public void resume(boolean newStart) { + setAction(ACTION_RESUME); if (checkConfig()) { StartCmd cmd = (StartCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START, @@ -75,11 +77,7 @@ public final class NormalController extends FeatureController implements INormal */ @Override public void cancel() { - if (checkConfig()) { - EventMsgUtil.getDefault() - .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, - checkTaskType())); - } + cancel(false); } /** @@ -87,6 +85,7 @@ public final class NormalController extends FeatureController implements INormal */ @Override public void reTry() { + setAction(ACTION_RETRY); if (checkConfig()) { int taskType = checkTaskType(); EventMsgUtil.getDefault() @@ -105,6 +104,7 @@ public final class NormalController extends FeatureController implements INormal */ @Override public void cancel(boolean removeFile) { + setAction(ACTION_CANCEL); if (checkConfig()) { CancelCmd cancelCmd = (CancelCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_CANCEL, @@ -118,15 +118,19 @@ public final class NormalController extends FeatureController implements INormal * 重新下载 */ @Override - public void reStart() { + public long reStart() { + setAction(ACTION_RESTART); if (checkConfig()) { EventMsgUtil.getDefault() .post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_RESTART, checkTaskType())); + return getEntity().getId(); } + return -1; } @Override public void save() { + setAction(ACTION_SAVE); if (!checkConfig()) { ALog.e(TAG, "保存修改失败"); } else { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 163af4ee..3bf9bfef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; +import com.arialyy.aria.core.common.controller.FeatureController; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.inf.ITargetHandler; @@ -34,12 +35,14 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private final String TAG = CommonUtil.getClassName(getClass()); private DTaskWrapper mWrapper; private DownloadEntity mEntity; + private int action; - public static CheckDEntityUtil newInstance(DTaskWrapper wrapper) { - return new CheckDEntityUtil(wrapper); + public static CheckDEntityUtil newInstance(DTaskWrapper wrapper, int action) { + return new CheckDEntityUtil(wrapper, action); } - private CheckDEntityUtil(DTaskWrapper wrapper) { + private CheckDEntityUtil(DTaskWrapper wrapper, int action) { + this.action = action; mWrapper = wrapper; mEntity = mWrapper.getEntity(); } @@ -80,19 +83,22 @@ public class CheckDEntityUtil implements ICheckEntityUtil { } else { m3U8Entity.update(); } - if (mWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { + if (mWrapper.getRequestType() == ITaskWrapper.M3U8_VOD + && action == FeatureController.ACTION_CREATE) { if (mEntity.getFileSize() == 0) { ALog.w(TAG, "由于m3u8协议的特殊性质,无法有效快速获取到正确到文件长度,如果你需要显示文件中长度,你需要自行设置文件长度:.asM3U8().asVod().setFileSize(xxx)"); } - } else if (mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { + } else if (mWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE + && action != FeatureController.ACTION_CANCEL) { if (file.exists()) { ALog.w(TAG, "对于直播来说,每次下载都是一个新文件,所以你需要设置新都文件路径,否则Aria框架将会覆盖已下载的文件"); file.delete(); } } - if (mWrapper.getM3U8Params().getHandler(IOptionConstant.bandWidthUrlConverter) != null + if (action != FeatureController.ACTION_CANCEL + && mWrapper.getM3U8Params().getHandler(IOptionConstant.bandWidthUrlConverter) != null && bandWidth == 0) { ALog.w(TAG, "你已经设置了码率url转换器,但是没有设置码率,Aria框架将采用第一个获取到的码率"); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 0e854888..1bf6b387 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; import com.arialyy.aria.core.common.RequestEnum; +import com.arialyy.aria.core.common.controller.FeatureController; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; @@ -41,12 +42,14 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { * 是否需要修改路径 */ private boolean needModifyPath = false; + private int action; - public static CheckDGEntityUtil newInstance(DGTaskWrapper wrapper) { - return new CheckDGEntityUtil(wrapper); + public static CheckDGEntityUtil newInstance(DGTaskWrapper wrapper, int action) { + return new CheckDGEntityUtil(wrapper, action); } - private CheckDGEntityUtil(DGTaskWrapper wrapper) { + private CheckDGEntityUtil(DGTaskWrapper wrapper, int action) { + this.action = action; mWrapper = wrapper; mEntity = mWrapper.getEntity(); } @@ -129,7 +132,9 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { return false; } - if (!mWrapper.isUnknownSize() && mEntity.getFileSize() == 0) { + if (action != FeatureController.ACTION_CANCEL + && !mWrapper.isUnknownSize() + && mEntity.getFileSize() == 0) { ALog.e(TAG, "组合任务必须设置文件文件大小,默认需要强制设置文件大小。如果无法获取到总长度,请调用#unknownSize()来标志该组合任务"); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java index 8f1cdf88..b46da62a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java @@ -17,7 +17,6 @@ package com.arialyy.aria.core.download; import android.text.TextUtils; import com.arialyy.aria.core.FtpUrlEntity; -import com.arialyy.aria.core.common.ErrorCode; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.orm.DbEntity; @@ -28,12 +27,14 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil { private final String TAG = "CheckFtpDirEntityUtil"; private DGTaskWrapper mWrapper; private DownloadGroupEntity mEntity; + private int action; - public static CheckFtpDirEntityUtil newInstance(DGTaskWrapper wrapper) { - return new CheckFtpDirEntityUtil(wrapper); + public static CheckFtpDirEntityUtil newInstance(DGTaskWrapper wrapper, int action) { + return new CheckFtpDirEntityUtil(wrapper, action); } - private CheckFtpDirEntityUtil(DGTaskWrapper wrapper) { + private CheckFtpDirEntityUtil(DGTaskWrapper wrapper, int action) { + this.action = action; mWrapper = wrapper; mEntity = mWrapper.getEntity(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index fe6dcfcf..ea3c768a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -16,10 +16,6 @@ package com.arialyy.aria.core.queue; -import com.arialyy.aria.core.task.DownloadGroupTask; -import com.arialyy.aria.core.task.DownloadTask; -import com.arialyy.aria.core.task.AbsTask; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.manager.TaskWrapperManager; @@ -29,7 +25,11 @@ import com.arialyy.aria.core.queue.pool.BaseExecutePool; import com.arialyy.aria.core.queue.pool.DGLoadSharePool; import com.arialyy.aria.core.queue.pool.DLoadSharePool; import com.arialyy.aria.core.queue.pool.UploadSharePool; +import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.core.task.DownloadGroupTask; +import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.task.UploadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; /** @@ -213,6 +213,10 @@ public abstract class AbsTaskQueue 0) { - client = newInstanceClient(urlEntity); - client.setConnectTimeout(mConnectTimeOut); // 连接10s超时 - InetAddress ip = InetAddress.getByName(urlEntity.hostName); - - client = connect(client, new InetAddress[] { ip }, 0, Integer.parseInt(urlEntity.port)); - mTaskOption.getUrlEntity().validAddr = ip; - } else { - DNSQueryThread dnsThread = new DNSQueryThread(urlEntity.hostName); - dnsThread.start(); - dnsThread.join(mConnectTimeOut); - InetAddress[] ips = dnsThread.getIps(); - client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port)); - } + if (CheckUtil.checkIp(urlEntity.hostName)) { + client = newInstanceClient(urlEntity); + client.setConnectTimeout(mConnectTimeOut); // 连接10s超时 + InetAddress ip = InetAddress.getByName(urlEntity.hostName); - if (client == null) { - failDownload(new AriaIOException(TAG, - String.format("链接失败, url: %s", mTaskOption.getUrlEntity().url)), false); - return null; - } + client = connect(client, new InetAddress[] { ip }, 0, Integer.parseInt(urlEntity.port)); + mTaskOption.getUrlEntity().validAddr = ip; + } else { + DNSQueryThread dnsThread = new DNSQueryThread(urlEntity.hostName); + dnsThread.start(); + dnsThread.join(mConnectTimeOut); + InetAddress[] ips = dnsThread.getIps(); + client = connect(newInstanceClient(urlEntity), ips, 0, Integer.parseInt(urlEntity.port)); + } - boolean loginSuccess = true; - if (urlEntity.needLogin) { - try { - if (TextUtils.isEmpty(urlEntity.account)) { - loginSuccess = client.login(urlEntity.user, urlEntity.password); - } else { - loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account); - } - } catch (IOException e) { - ALog.e(TAG, - new TaskException(TAG, String.format("登录失败,错误码为:%s, msg:%s", client.getReplyCode(), - client.getReplyString()), e)); - return null; - } - } + if (client == null) { + failDownload(client, String.format("链接失败, url: %s", mTaskOption.getUrlEntity().url), null, + true); + return null; + } - if (!loginSuccess) { - failDownload( + boolean loginSuccess = true; + if (urlEntity.needLogin) { + try { + if (TextUtils.isEmpty(urlEntity.account)) { + loginSuccess = client.login(urlEntity.user, urlEntity.password); + } else { + loginSuccess = client.login(urlEntity.user, urlEntity.password, urlEntity.account); + } + } catch (IOException e) { + ALog.e(TAG, new TaskException(TAG, String.format("登录失败,错误码为:%s, msg:%s", client.getReplyCode(), - client.getReplyString())), - false); - client.disconnect(); + client.getReplyString()), e)); return null; } + } - int reply = client.getReplyCode(); - if (!FTPReply.isPositiveCompletion(reply)) { - client.disconnect(); - failDownload(new AriaIOException(TAG, - String.format("无法连接到ftp服务器,filePath: %s, url: %s, errorCode: %s, errorMsg:%s", - mEntity.getKey(), mTaskOption.getUrlEntity().url, reply, - client.getReplyString())), - true); - return null; + if (!loginSuccess) { + failDownload(client, "登录失败", null, false); + client.disconnect(); + return null; + } + + int reply = client.getReplyCode(); + if (!FTPReply.isPositiveCompletion(reply)) { + failDownload(client, String.format("无法连接到ftp服务器,filePath: %s, url: %s", mEntity.getKey(), + mTaskOption.getUrlEntity().url), null, true); + client.disconnect(); + return null; + } + // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码 + charSet = "UTF-8"; + reply = client.sendCommand("OPTS UTF8", "ON"); + if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) { + ALog.i(TAG, "D_FTP 服务器不支持开启UTF8编码,尝试使用Aria手动设置的编码"); + if (!TextUtils.isEmpty(mTaskOption.getCharSet())) { + charSet = mTaskOption.getCharSet(); } - // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码 - charSet = "UTF-8"; - reply = client.sendCommand("OPTS UTF8", "ON"); - if (reply != FTPReply.COMMAND_IS_SUPERFLUOUS) { - ALog.i(TAG, "D_FTP 服务器不支持开启UTF8编码,尝试使用Aria手动设置的编码"); - if (!TextUtils.isEmpty(mTaskOption.getCharSet())) { - charSet = mTaskOption.getCharSet(); - } + } + client.setControlEncoding(charSet); + client.setDataTimeout(10 * 1000); + if (mTaskOption.getConnMode() == FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) { + client.enterLocalActiveMode(); + if (mTaskOption.getMinPort() != 0 && mTaskOption.getMaxPort() != 0) { + client.setActivePortRange(mTaskOption.getMinPort(), mTaskOption.getMaxPort()); + } + if (!TextUtils.isEmpty(mTaskOption.getActiveExternalIPAddress())) { + client.setActiveExternalIPAddress(mTaskOption.getActiveExternalIPAddress()); } - client.setControlEncoding(charSet); - client.setDataTimeout(10 * 1000); + } else { client.enterLocalPassiveMode(); - client.setFileType(FTP.BINARY_FILE_TYPE); - } catch (IOException e) { - closeClient(client); - e.printStackTrace(); - } catch (InterruptedException e) { - closeClient(client); - e.printStackTrace(); } + client.setFileType(FTP.BINARY_FILE_TYPE); + return client; } @@ -387,9 +379,19 @@ public abstract class AbsFtpInfoThread 15) { + return false; + } + Pattern p = Pattern.compile(Regular.REG_IP_V4); + Matcher m = p.matcher(ip); + return m.find() && m.groupCount() > 0; + } + + /** + * 判断http请求是否有效 + * {@link HttpURLConnection#HTTP_BAD_GATEWAY} or {@link HttpURLConnection#HTTP_BAD_METHOD} + * or {@link HttpURLConnection#HTTP_BAD_REQUEST} 无法重试 + * + * @param errorCode http 返回码 + * @return {@code true} 无效请求;{@code false} 有效请求 + */ + public static boolean httpIsBadRequest(int errorCode) { + return errorCode == HttpURLConnection.HTTP_BAD_GATEWAY + || errorCode == HttpURLConnection.HTTP_BAD_METHOD + || errorCode == HttpURLConnection.HTTP_BAD_REQUEST; + } + + /** + * 判断ftp请求是否有效 + * + * @return {@code true} 无效请求;{@code false} 有效请求 + */ + public static boolean ftpIsBadRequest(int errorCode) { + return errorCode >= 400 && errorCode < 600; + } + /** * 检查和处理下载任务的路径冲突 * diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index f1c4cef1..8efca66d 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -23,6 +23,7 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.common.FtpConnectionMode; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; @@ -80,11 +81,11 @@ public class FtpDownloadActivity extends BaseActivity getFtpDownloadInfo(Context context) { - String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); + //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); + String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath); singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); diff --git a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java index aad5bc82..869febdc 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/HttpDownloadModule.java @@ -54,11 +54,12 @@ public class HttpDownloadModule extends BaseViewModule { //String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk"; //String url = "http://fdfs.speedata.cn:9989/group1/M00/00/05/rBGFrl3fdAKAVJwfMtSa9R18wLU139.zip"; //String url = "http://9.9.9.28:8088/files/update.zip"; - String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; + //String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; //String url = "https://imtt.dd.qq.com/16891/apk/70BFFDB05AB8686F2A4CF3E07588A377.apk?fsname=com.tencent.tmgp.speedmobile_1.16.0.33877_1160033877.apk&csr=1bbd"; //String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg"; //String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath); - String filePath = "/mnt/sdcard/gg.png"; + String url = "https://y.qq.com/download/import/QQMusic-import-1.2.1.zip"; + String filePath = "/mnt/sdcard/QQMusic-import-1.2.1.zip"; //String filePath = "/mnt/sdcard/update.zip"; singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index d72aa415..c6b238f6 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -32,6 +32,7 @@ import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.HttpOption; +import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.listener.ISchedulers; @@ -116,11 +117,6 @@ public class SingleTaskActivity extends BaseActivity { } }); getBinding().setViewModel(this); - try { - getBinding().codeView.setSource(AppUtil.getHelpCode(this, "HttpDownload.java")); - } catch (IOException e) { - e.printStackTrace(); - } } public void chooseUrl() { @@ -279,10 +275,9 @@ public class SingleTaskActivity extends BaseActivity { .load(mTaskId) .stop(); } else { - mUrl = "http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk"; - Aria.download(this).load(mTaskId) + mTaskId = Aria.download(this).load(mTaskId) //.updateUrl(mUrl) - .resume(); + .reStart(); } break; case R.id.cancel: @@ -296,6 +291,7 @@ public class SingleTaskActivity extends BaseActivity { option.addHeader("1", "@") .useServerFileName(true) .setFileLenAdapter(new FileLenAdapter()); + //option.setRequestType(RequestEnum.POST); mTaskId = Aria.download(SingleTaskActivity.this) .load(mUrl) .setFilePath(mFilePath, true) diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java index 0e90f9a5..4d099c82 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java @@ -298,7 +298,7 @@ public class DownloadAdapter extends AbsRVAdapter - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_single.xml b/app/src/main/res/layout/activity_single.xml index 7716da64..0b679ab9 100644 --- a/app/src/main/res/layout/activity_single.xml +++ b/app/src/main/res/layout/activity_single.xml @@ -76,11 +76,5 @@ bind:stateStr="@{stateStr}" /> - - \ No newline at end of file From 27c889e171d125c3d134a2caaba35f3936c78433 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Tue, 17 Dec 2019 22:04:21 +0800 Subject: [PATCH 037/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dftp=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=97=A0=E6=B3=95=E5=93=8D=E5=BA=94`abor`=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=AF=BC=E8=87=B4=E7=9A=84=E6=97=A0=E6=B3=95=E5=81=9C?= =?UTF-8?q?=E6=AD=A2=E4=B8=8A=E4=BC=A0=E7=9A=84=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dftp=E4=B8=8A=E4=BC=A0=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E6=9C=89=E9=95=BF=E5=BA=A6=E4=B8=BA?= =?UTF-8?q?0=E7=9A=84=E6=96=87=E4=BB=B6=E5=AF=BC=E8=87=B4=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8B=E8=BD=BD=E4=BB=BB=E5=8A=A1=E5=92=8C?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=BB=E5=8A=A1=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=98=AF=E5=90=8C=E4=B8=80=E4=B8=AA=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=E7=9A=84=E8=AE=B0=E5=BD=95=E6=B7=B7?= =?UTF-8?q?=E4=B9=B1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/CheckDEntityUtil.java | 4 +- .../aria/core/download/CheckDGEntityUtil.java | 2 +- .../download/target/FtpBuilderTarget.java | 1 + .../download/target/FtpDirConfigHandler.java | 3 +- .../download/target/HttpBuilderTarget.java | 2 + .../target/HttpGroupConfigHandler.java | 2 + .../download/target/TcpBuilderTarget.java | 2 + .../core/manager/DTaskWrapperFactory.java | 5 +- .../core/upload/target/FtpBuilderTarget.java | 2 + .../core/upload/target/HttpBuilderTarget.java | 6 +- DEV_LOG.md | 7 +- .../arialyy/aria/ftp/AbsFtpInfoThread.java | 7 +- .../arialyy/aria/ftp/FtpRecordAdapter.java | 4 +- .../ftp/download/FtpDThreadTaskAdapter.java | 4 +- .../aria/ftp/upload/FtpUFileInfoThread.java | 22 ++--- .../ftp/upload/FtpUThreadTaskAdapter.java | 31 ++++--- .../arialyy/aria/http/HttpFileInfoThread.java | 2 +- .../arialyy/aria/http/HttpRecordAdapter.java | 4 +- .../http/download/HttpDThreadTaskAdapter.java | 4 +- .../arialyy/aria/m3u8/M3U8RecordAdapter.java | 10 +-- .../aria/m3u8/live/M3U8LiveLoader.java | 2 +- .../com/arialyy/aria/core/TaskRecord.java | 12 +-- .../com/arialyy/aria/core/ThreadRecord.java | 3 +- .../aria/core/common/AbsNormalEntity.java | 15 ++++ .../aria/core/common/RecordHandler.java | 6 +- .../aria/core/download/DownloadEntity.java | 32 ++++---- .../aria/core/download/M3U8Entity.java | 4 +- .../aria/core/upload/UploadEntity.java | 14 ++-- .../com/arialyy/aria/orm/AbsDelegate.java | 11 +-- .../java/com/arialyy/aria/orm/DBConfig.java | 2 +- .../com/arialyy/aria/orm/DelegateCommon.java | 2 +- .../com/arialyy/aria/orm/DelegateFind.java | 4 +- .../com/arialyy/aria/orm/DelegateUpdate.java | 4 +- .../java/com/arialyy/aria/orm/SqlHelper.java | 80 +++++++++++++++++++ .../java/com/arialyy/aria/orm/SqlUtil.java | 12 +++ .../java/com/arialyy/aria/util/CheckUtil.java | 11 ++- .../com/arialyy/aria/util/DbDataHelper.java | 20 +++-- .../com/arialyy/aria/util/RecordUtil.java | 8 +- README.md | 38 +++++---- .../simple/core/upload/FtpUploadActivity.java | 15 ++-- .../simple/core/upload/UploadModule.java | 9 ++- .../main/res/layout/activity_ftp_upload.xml | 10 +-- build.gradle | 2 +- 43 files changed, 295 insertions(+), 145 deletions(-) diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 3bf9bfef..a421deef 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -157,11 +157,11 @@ public class CheckDEntityUtil implements ICheckEntityUtil { File oldFile = new File(mEntity.getFilePath()); if (oldFile.exists()) { // 处理普通任务的重命名 - RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath()); + RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath(), mEntity.getTaskType()); ALog.i(TAG, String.format("将任务重命名为:%s", newFile.getName())); } else if (RecordUtil.blockTaskExists(oldFile.getPath())) { // 处理分块任务的重命名 - RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath()); + RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath(), mEntity.getTaskType()); ALog.i(TAG, String.format("将分块任务重命名为:%s", newFile.getName())); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 1bf6b387..2834c5c3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -179,7 +179,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { return; } - RecordUtil.modifyTaskRecord(oldPath, newPath); + RecordUtil.modifyTaskRecord(oldPath, newPath, mEntity.getTaskType()); entity.setFilePath(newPath); entity.setFileName(newName); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java index a67a8bb7..31106b59 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpBuilderTarget.java @@ -31,6 +31,7 @@ public class FtpBuilderTarget extends AbsBuilderTarget { mConfigHandler = new DNormalConfigHandler<>(this, -1); mConfigHandler.setUrl(url); getTaskWrapper().setRequestType(ITaskWrapper.D_FTP); + getEntity().setTaskType(ITaskWrapper.D_FTP); getTaskWrapper().setNewTask(true); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java index f731e41b..049001fd 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java @@ -33,11 +33,12 @@ class FtpDirConfigHandler extends AbsGroupConfigHandle } private void init() { - getTaskWrapper().setRequestType(AbsTaskWrapper.D_FTP_DIR); + getTaskWrapper().setRequestType(ITaskWrapper.D_FTP_DIR); List wrappers = getTaskWrapper().getSubTaskWrapper(); if (!wrappers.isEmpty()) { for (DTaskWrapper subWrapper : wrappers) { subWrapper.setRequestType(ITaskWrapper.D_FTP); + subWrapper.getEntity().setTaskType(ITaskWrapper.D_FTP); } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java index 534d98b0..10d07f42 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpBuilderTarget.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.m3u8.M3U8LiveOption; import com.arialyy.aria.core.download.m3u8.M3U8VodOption; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -32,6 +33,7 @@ public class HttpBuilderTarget extends AbsBuilderTarget { mConfigHandler.setUrl(url); getTaskWrapper().setRequestType(ITaskWrapper.D_HTTP); getTaskWrapper().setNewTask(true); + ((DownloadEntity)getEntity()).setTaskType(ITaskWrapper.D_HTTP); } public HttpBuilderTarget m3u8VodOption(M3U8VodOption m3U8VodOption) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java index 235ff97a..77249633 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/HttpGroupConfigHandler.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.AbsTarget; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -58,6 +59,7 @@ class HttpGroupConfigHandler extends AbsGroupConfigHan List subEntities = DbDataHelper.createHttpSubTask(groupHash, mUrls); List wrappers = new ArrayList<>(); for (DownloadEntity subEntity : subEntities) { + subEntity.setTaskType(ITaskWrapper.D_HTTP); wrappers.add(new DTaskWrapper(subEntity)); } getEntity().setUrls(urls); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java index 77b0a8ea..ab9d7212 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/TcpBuilderTarget.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsBuilderTarget; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.wrapper.ITaskWrapper; /** @@ -29,6 +30,7 @@ public class TcpBuilderTarget extends AbsBuilderTarget { TcpBuilderTarget(String ip, int port) { mConfigHandler = new DNormalConfigHandler<>(this, -1); getTaskWrapper().setRequestType(ITaskWrapper.D_TCP); + ((DownloadEntity) getEntity()).setTaskType(ITaskWrapper.D_TCP); getTaskWrapper().setNewTask(true); } // diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java index c974d75c..16e6592d 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java @@ -20,6 +20,7 @@ import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.io.File; /** @@ -71,7 +72,7 @@ class DTaskWrapperFactory implements INormalTEFactory { mConfigHandler = new UNormalConfigHandler<>(this, -1); mConfigHandler.setFilePath(filePath); getTaskWrapper().setRequestType(ITaskWrapper.U_FTP); + ((UploadEntity)getEntity()).setTaskType(ITaskWrapper.U_FTP); getTaskWrapper().setNewTask(true); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java index 68145b56..4940a4c8 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/target/HttpBuilderTarget.java @@ -17,7 +17,8 @@ package com.arialyy.aria.core.upload.target; import com.arialyy.aria.core.common.AbsBuilderTarget; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; /** * Created by lyy on 2017/2/28. @@ -31,7 +32,8 @@ public class HttpBuilderTarget extends AbsBuilderTarget { mConfigHandler.setFilePath(filePath); //http暂时不支持断点上传 getTaskWrapper().setSupportBP(false); - getTaskWrapper().setRequestType(AbsTaskWrapper.U_HTTP); + getTaskWrapper().setRequestType(ITaskWrapper.U_HTTP); + ((UploadEntity) getEntity()).setTaskType(ITaskWrapper.U_HTTP); getTaskWrapper().setNewTask(true); } diff --git a/DEV_LOG.md b/DEV_LOG.md index 85067cc1..bd29e876 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,13 +1,16 @@ ## 开发日志 - + v_3.8 + + v_3.8 (2019/12/17) - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 - 修复一个在xml中使用fragment导致的内存泄漏问题 - m3u8协议的key信息增加了`keyFormat`,`keyFormatVersion`字段 - - m3u8增加了`ignoreFailureTs`方法,忽略虾类失败的ts切片 + - m3u8增加了`ignoreFailureTs`方法,忽略下载失败的ts切片 - 修复在dialogFragment的`onCreateDialog()`注册导致的注解不生效问题 - 修复组合任务初始化失败时,无法删除的问题 - 修复`reStart()`后,无法停止的问题 - ftp增加主动模式,开启主动模式:https://aria.laoyuyu.me/aria_doc/api/ftp_params.html + - 修复ftp服务器无法响应`abor`命令导致的无法停止上传的问题 https://github.com/AriaLyy/Aria/issues/564 + - 修复ftp上传时,服务器有长度为0的文件导致上传失败的问题 + - 修复下载任务和上传任务的文件路径是同一个时,导致的记录混乱问题 - 优化提示 + v_3.7.10 (2019/12/3) - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java index cc74ee94..3fa81ac6 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java @@ -50,7 +50,7 @@ import javax.net.ssl.SSLContext; public abstract class AbsFtpInfoThread> implements Runnable { - private final String TAG = CommonUtil.getClassName(getClass()); + protected final String TAG = CommonUtil.getClassName(getClass()); protected ENTITY mEntity; protected TASK_WRAPPER mTaskWrapper; protected FtpTaskOption mTaskOption; @@ -123,7 +123,7 @@ public abstract class AbsFtpInfoThread 0){ + file.seek(getThreadRecord().startLocation); + } byte[] buffer = new byte[getTaskConfig().getBuffSize()]; int len; while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java index 29f1cdc7..af113b53 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java @@ -22,11 +22,11 @@ import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.processor.FtpInterceptHandler; +import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.ftp.AbsFtpInfoThread; -import com.arialyy.aria.core.processor.FtpInterceptHandler; -import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.DbDataHelper; @@ -39,7 +39,6 @@ import java.util.List; * 单任务上传远程服务器文件信息 */ class FtpUFileInfoThread extends AbsFtpInfoThread { - private static final String TAG = "FtpUploadFileInfoThread"; static final int CODE_COMPLETE = 0xab1; private boolean isComplete = false; private String remotePath; @@ -59,10 +58,10 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { @Override protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) { // 旧任务将不做处理,否则断点续传上传将失效 - if (!mTaskWrapper.isNewTask()) { - ALog.d(TAG, "任务是旧任务,忽略该拦截器"); - return true; - } + //if (!mTaskWrapper.isNewTask()) { + // ALog.d(TAG, "任务是旧任务,忽略该拦截器"); + // return true; + //} try { IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor(); if (interceptor != null) { @@ -78,7 +77,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { FtpInterceptHandler interceptHandler = interceptor.onIntercept(mEntity, files); /* - 处理远端有同名文件的情况 + * 处理远端有同名文件的情况 */ if (files.contains(mEntity.getFileName())) { if (interceptHandler.isCoverServerFile()) { @@ -94,6 +93,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { + "/" + interceptHandler.getNewFileName(); mTaskOption.setNewFileName(interceptHandler.getNewFileName()); + closeClient(client); run(); return false; @@ -123,6 +123,9 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { if (ftpFile.getSize() == mEntity.getFileSize()) { isComplete = true; ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】"); + } else if (ftpFile.getSize() == 0) { + mTaskWrapper.setNewTask(true); + ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】,但文件长度为0,重新上传该文件"); } else { ALog.w(TAG, "FTP服务器已存在未完成的文件【" + ftpFile.getName() @@ -135,7 +138,8 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { mTaskWrapper.setNewTask(false); // 修改记录 - TaskRecord record = DbDataHelper.getTaskRecord(mTaskWrapper.getKey()); + TaskRecord record = DbDataHelper.getTaskRecord(mTaskWrapper.getKey(), + mTaskWrapper.getEntity().getTaskType()); if (record == null) { record = new TaskRecord(); record.fileName = mEntity.getFileName(); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index 891170ac..bd5e4bb7 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -34,6 +34,7 @@ import java.io.UnsupportedEncodingException; */ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private String dir, remotePath; + private boolean storeFail = false; FtpUThreadTaskAdapter(SubThreadConfig config) { super(config); @@ -67,8 +68,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { file = new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); - if (getThreadRecord().startLocation != 0) { - //file.skipBytes((int) getThreadConfig().START_LOCATION); + if (getThreadRecord().startLocation > 0) { file.seek(getThreadRecord().startLocation); } boolean complete = upload(client, file); @@ -79,6 +79,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { String.format("任务【%s】线程__%s__上传完毕", getEntity().getKey(), getThreadRecord().threadId)); complete(); } catch (IOException e) { + e.printStackTrace(); fail(new AriaIOException(TAG, String.format("上传失败,filePath: %s, uploadUrl: %s", getEntity().getFilePath(), getThreadConfig().url)), true); @@ -118,10 +119,11 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { */ private boolean upload(final FTPClient client, final BufferedRandomAccessFile bis) throws IOException { - + final FtpFISAdapter fa = new FtpFISAdapter(bis); + storeFail = false; try { ALog.d(TAG, String.format("remotePath: %s", remotePath)); - client.storeFile(remotePath, new FtpFISAdapter(bis), new OnFtpInputStreamListener() { + client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { boolean isStoped = false; @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, @@ -137,23 +139,30 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { progress(bytesTransferred); } catch (IOException e) { e.printStackTrace(); + storeFail = true; + try { + fa.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + closeClient(client); } } }); } catch (IOException e) { String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReplyCode(), client.getReplyString(), getEntity().getFilePath()); - if (client.isConnected()) { - client.disconnect(); - } + closeClient(client); if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); } else { - fail(new AriaIOException(TAG, msg, e), true); + fail(new AriaIOException(TAG, msg, e), !storeFail); } return false; } - + if (storeFail) { + return false; + } int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { if (reply != FTPReply.TRANSFER_ABORTED) { @@ -161,9 +170,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", reply, client.getReplyString(), getEntity().getFilePath())), false); } - if (client.isConnected()) { - client.disconnect(); - } + closeClient(client); return false; } return true; diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java index d679eef4..9c7103e0 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java @@ -277,7 +277,7 @@ public class HttpFileInfoThread implements Runnable { } mEntity.setFileName(newName); mEntity.setFilePath(newPath); - RecordUtil.modifyTaskRecord(oldFile.getPath(), newPath); + RecordUtil.modifyTaskRecord(oldFile.getPath(), newPath, mEntity.getTaskType()); } /** diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java index 21df6087..1dbba97b 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java @@ -67,7 +67,7 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { tr.startLocation = startL; tr.isComplete = false; - tr.threadType = TaskRecord.TYPE_HTTP_FTP; + tr.threadType = getEntity().getTaskType(); //最后一个线程的结束位置即为文件的总长度 if (threadId == (record.threadNum - 1)) { endL = getEntity().getFileSize(); @@ -91,7 +91,7 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter { } else { record.isBlock = false; } - record.taskType = TaskRecord.TYPE_HTTP_FTP; + record.taskType = getEntity().getTaskType(); record.isGroupRecord = getEntity().isGroupChild(); if (record.isGroupRecord) { if (getEntity() instanceof DownloadEntity) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index 3f5ca877..45ea172a 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -112,7 +112,9 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", getTaskConfig().getBuffSize()); //设置每条线程写入文件的位置 - file.seek(getThreadRecord().startLocation); + if (getThreadRecord().startLocation > 0){ + file.seek(getThreadRecord().startLocation); + } readNormal(is, file); handleComplete(); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 088f5f9d..17cc04e3 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -106,7 +106,7 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { tr.threadId = threadId; tr.isComplete = false; tr.startLocation = 0; - tr.threadType = TaskRecord.TYPE_M3U8_VOD; + tr.threadType = getEntity().getTaskType(); tr.tsUrl = mOption.getUrls().get(threadId); return tr; } @@ -118,13 +118,7 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { record.threadRecords = new ArrayList<>(); record.threadNum = threadNum; record.isBlock = true; - - int requestType = getWrapper().getRequestType(); - if (requestType == ITaskWrapper.M3U8_VOD) { - record.taskType = TaskRecord.TYPE_M3U8_VOD; - } else if (requestType == ITaskWrapper.M3U8_LIVE) { - record.taskType = TaskRecord.TYPE_M3U8_LIVE; - } + record.taskType = getEntity().getTaskType(); record.bandWidth = mOption.getBandWidth(); return record; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 09d96c28..81af7931 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -169,7 +169,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { record.taskKey = mRecord.filePath; record.isComplete = false; record.tsUrl = tsUrl; - record.threadType = TaskRecord.TYPE_M3U8_LIVE; + record.threadType = getEntity().getTaskType(); record.threadId = indexId; mRecord.threadRecords.add(record); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java index 222c395b..75d3d92e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/TaskRecord.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.annotation.Ignore; import com.arialyy.aria.orm.annotation.NoNull; @@ -26,9 +27,9 @@ import java.util.List; * 任务上传或下载的任务记录 */ public class TaskRecord extends DbEntity { - public static final int TYPE_HTTP_FTP = 0; - public static final int TYPE_M3U8_VOD = 1; - public static final int TYPE_M3U8_LIVE = 2; + //public static final int TYPE_HTTP_FTP = 0; + //public static final int TYPE_M3U8_VOD = 1; + //public static final int TYPE_M3U8_LIVE = 2; @Ignore public List threadRecords; @@ -41,7 +42,6 @@ public class TaskRecord extends DbEntity { /** * 任务文件路径 */ - @Unique public String filePath; /** @@ -79,8 +79,8 @@ public class TaskRecord extends DbEntity { public boolean isBlock = false; /** - * 线程类型 - * {@link #TYPE_HTTP_FTP}、{@link #TYPE_M3U8_VOD} + * 任务类型 + * {@link ITaskWrapper} */ public int taskType = 0; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java b/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java index aca2b47f..546172d7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/ThreadRecord.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.core; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; /** @@ -56,7 +57,7 @@ public class ThreadRecord extends DbEntity { /** * 线程类型 - * {@link TaskRecord#TYPE_HTTP_FTP}、{@link TaskRecord#TYPE_M3U8_VOD} + * {@link ITaskWrapper} */ public int threadType = 0; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java index d26bce47..015a0fcf 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsNormalEntity.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.common; import android.os.Parcel; import android.os.Parcelable; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Default; /** @@ -44,6 +45,20 @@ public abstract class AbsNormalEntity extends AbsEntity implements Parcelable { private boolean isRedirect = false; //是否重定向 private String redirectUrl; //重定向链接 + /** + * 任务类型 + * {@link ITaskWrapper} + */ + private int taskType; + + @Override public int getTaskType() { + return taskType; + } + + public void setTaskType(int taskType) { + this.taskType = taskType; + } + public String getUrl() { return url; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index 4672a32a..dfe8f2d7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -71,9 +71,11 @@ public class RecordHandler implements IRecordHandler { convertDb(); } else { mAdapter.onPre(); - mTaskRecord = DbDataHelper.getTaskRecord(getFilePath()); + mTaskRecord = DbDataHelper.getTaskRecord(getFilePath(), mEntity.getTaskType()); if (mTaskRecord == null) { - FileUtil.createFile(getFilePath()); + if (!new File(getFilePath()).exists()){ + FileUtil.createFile(getFilePath()); + } initRecord(true); } else { File file = new File(mTaskRecord.filePath); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index 01190f61..b6264596 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -78,22 +78,22 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { return getUrl(); } - @Override public int getTaskType() { - int type; - if (TextUtils.isEmpty(getUrl())) { - type = ITaskWrapper.ERROR; - } else if (getUrl().startsWith("ftp")) { - type = ITaskWrapper.D_FTP; - } else { - M3U8Entity temp = getM3U8Entity(); - if (temp == null) { - type = ITaskWrapper.D_HTTP; - } else { - type = temp.isLive() ? ITaskWrapper.M3U8_LIVE : ITaskWrapper.M3U8_VOD; - } - } - return type; - } + //@Override public int getTaskType() { + // int type; + // if (TextUtils.isEmpty(getUrl())) { + // type = ITaskWrapper.ERROR; + // } else if (getUrl().startsWith("ftp")) { + // type = ITaskWrapper.D_FTP; + // } else { + // M3U8Entity temp = getM3U8Entity(); + // if (temp == null) { + // type = ITaskWrapper.D_HTTP; + // } else { + // type = temp.isLive() ? ITaskWrapper.M3U8_LIVE : ITaskWrapper.M3U8_VOD; + // } + // } + // return type; + //} public DownloadEntity() { } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java index d024cb43..b534099b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java @@ -20,6 +20,7 @@ import android.os.Parcelable; import android.text.TextUtils; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.orm.annotation.Default; import com.arialyy.aria.util.ALog; @@ -153,7 +154,8 @@ public class M3U8Entity extends DbEntity implements Parcelable { return null; } List peers = new ArrayList<>(); - TaskRecord taskRecord = DbDataHelper.getTaskRecord(filePath); + TaskRecord taskRecord = DbDataHelper.getTaskRecord(filePath, + isLive ? ITaskWrapper.M3U8_LIVE : ITaskWrapper.M3U8_VOD); File cacheDir = new File(getCacheDir()); if ((taskRecord == null || taskRecord.threadRecords == null diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java index 27db92a2..16f70083 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/upload/UploadEntity.java @@ -17,9 +17,7 @@ package com.arialyy.aria.core.upload; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; import com.arialyy.aria.core.common.AbsNormalEntity; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.annotation.Primary; /** @@ -58,12 +56,12 @@ public class UploadEntity extends AbsNormalEntity implements Parcelable { return filePath; } - @Override public int getTaskType() { - if (TextUtils.isEmpty(getUrl())){ - return ITaskWrapper.ERROR; - } - return getUrl().startsWith("ftp") ? ITaskWrapper.U_FTP : ITaskWrapper.U_HTTP; - } + //@Override public int getTaskType() { + // if (TextUtils.isEmpty(getUrl())){ + // return ITaskWrapper.ERROR; + // } + // return getUrl().startsWith("ftp") ? ITaskWrapper.U_FTP : ITaskWrapper.U_HTTP; + //} public UploadEntity() { } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java index 72f19d55..132139f3 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java @@ -28,16 +28,7 @@ import java.net.URLEncoder; abstract class AbsDelegate { static final String TAG = "AbsDelegate"; - /** - * URL编码字符串 - * - * @param str 原始字符串 - * @return 编码后的字符串 - */ - String encodeStr(String str) { - str = str.replaceAll("\\\\+", "%2B"); - return URLEncoder.encode(str); - } + /** * 检查list参数是否合法,list只能是{@code List} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java index 7ab0b3bc..641bd1f1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -34,7 +34,7 @@ class DBConfig { static boolean DEBUG = false; static Map> mapping = new LinkedHashMap<>(); static String DB_NAME; - static int VERSION = 56; + static int VERSION = 57; /** * 是否将数据库保存在Sd卡,{@code true} 是 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java index 27a01d3f..d0205bf8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java @@ -113,7 +113,7 @@ class DelegateCommon extends AbsDelegate { sql = sql.replace("?", "%s"); Object[] params = new String[expression.length - 1]; for (int i = 0, len = params.length; i < len; i++) { - params[i] = String.format("'%s'", encodeStr(expression[i + 1])); + params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1])); } sql = String.format(sql, params); Cursor cursor = db.rawQuery(sql, null); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java index 6fdd6695..0e6a26e6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -205,7 +205,7 @@ class DelegateFind extends AbsDelegate { sql = sql.replace("?", "%s"); Object[] params = new String[expression.length - 1]; for (int i = 0, len = params.length; i < len; i++) { - params[i] = String.format("'%s'", encodeStr(expression[i + 1])); + params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1])); } sql = String.format(sql, params); } else { @@ -421,7 +421,7 @@ class DelegateFind extends AbsDelegate { String[] temp = new String[selectionArgs.length]; int i = 0; for (String arg : selectionArgs) { - temp[i] = encodeStr(arg); + temp[i] = SqlUtil.encodeStr(arg); i++; } Cursor cursor = db.rawQuery(sql, temp); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java index 9835b250..00e32002 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -47,7 +47,7 @@ class DelegateUpdate extends AbsDelegate { sql = sql.replace("?", "%s"); Object[] params = new String[expression.length - 1]; for (int i = 0, len = params.length; i < len; i++) { - params[i] = String.format("'%s'", encodeStr(expression[i + 1])); + params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1])); } sql = String.format(sql, params); db.execSQL(sql); @@ -166,7 +166,7 @@ class DelegateUpdate extends AbsDelegate { value = field.get(dbEntity).toString(); } } - values.put(field.getName(), encodeStr(value)); + values.put(field.getName(), SqlUtil.encodeStr(value)); } return values; } catch (IllegalAccessException e) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 0344b32b..546a84c0 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -21,6 +21,9 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Build; +import android.text.TextUtils; +import com.arialyy.aria.core.download.M3U8Entity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import java.io.File; import java.util.ArrayList; @@ -102,6 +105,10 @@ final class SqlHelper extends SQLiteOpenHelper { } else { handleDbUpdate(db, null); } + // 处理380版本TaskRecord 增加的记录类型判断 + if (newVersion == 57) { + addTaskRecordType(db); + } } } @@ -256,6 +263,79 @@ final class SqlHelper extends SQLiteOpenHelper { return temp; } + /** + * 给TaskRecord 增加任务类型 + */ + private void addTaskRecordType(SQLiteDatabase db) { + try { + db.beginTransaction(); + /* + * 增加下载实体的类型 + */ + String dSql = "SELECT downloadPath, url FROM DownloadEntity"; + Cursor c = db.rawQuery(dSql, null); + while (c.moveToNext()) { + int type; + String filePath = c.getString(0); + String url = c.getString(1); + if (url.startsWith("ftp") || url.startsWith("sftp")) { + type = ITaskWrapper.D_FTP; + } else { + if (mDelegate.tableExists(db, M3U8Entity.class)) { + Cursor m3u8c = db.rawQuery("SELECT isLive FROM M3U8Entity WHERE filePath=\"" + + SqlUtil.encodeStr(filePath) + + "\"", null); + if (m3u8c.moveToNext()) { + String temp = m3u8c.getString(0); + type = + (TextUtils.isEmpty(temp) ? false : Boolean.valueOf(temp)) ? ITaskWrapper.M3U8_LIVE + : ITaskWrapper.M3U8_VOD; + } else { + type = ITaskWrapper.D_HTTP; + } + m3u8c.close(); + } else { + type = ITaskWrapper.D_HTTP; + } + } + db.execSQL("UPDATE DownloadEntity SET taskType=? WHERE downloadPath=?", + new Object[] { type, filePath }); + db.execSQL("UPDATE TaskRecord SET taskType=? WHERE filePath=?", + new Object[] { type, filePath }); + db.execSQL("UPDATE ThreadRecord SET threadType=? WHERE taskKey=?", + new Object[] { type, filePath }); + } + c.close(); + + /* + * 增加上传实体的类型 + */ + String uSql = "SELECT filePath, url FROM UploadEntity"; + c = db.rawQuery(uSql, null); + while (c.moveToNext()) { + int type; + String filePath = c.getString(c.getColumnIndex("filePath")); + String url = c.getString(c.getColumnIndex("url")); + if (url.startsWith("ftp") || url.startsWith("sftp")) { + type = ITaskWrapper.D_FTP; + } else { + type = ITaskWrapper.D_HTTP; + } + db.execSQL("UPDATE UploadEntity SET taskType=? WHERE filePath=?", + new Object[] { type, filePath }); + db.execSQL("UPDATE TaskRecord SET taskType=? WHERE filePath=?", + new Object[] { type, filePath }); + db.execSQL("UPDATE ThreadRecord SET threadType=? WHERE taskKey=?", + new Object[] { type, filePath }); + } + c.close(); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + /** * 删除重复的repeat数据 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java index 0dea4c5d..c112784f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -29,6 +29,7 @@ import com.arialyy.aria.orm.annotation.Wrapper; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -41,6 +42,17 @@ import java.util.Set; */ final class SqlUtil { + /** + * URL编码字符串 + * + * @param str 原始字符串 + * @return 编码后的字符串 + */ + static String encodeStr(String str) { + str = str.replaceAll("\\\\+", "%2B"); + return URLEncoder.encode(str); + } + /** * 获取主键字段名 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 986cdfc5..d71c5003 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -141,8 +141,15 @@ public class CheckUtil { */ public static void checkMemberClass(Class clazz) { int modifiers = clazz.getModifiers(); - if (!clazz.isMemberClass() || !Modifier.isStatic(modifiers) || Modifier.isPrivate(modifiers)) { - ALog.e(TAG, "为了防止内存泄漏,请使用静态的成员类(public static class xxx)或文件类(A.java)"); + //ALog.d(TAG, "isMemberClass = " + // + clazz.isMemberClass() + // + "; isStatic = " + // + Modifier.isStatic(modifiers) + // + "; isPrivate = " + // + Modifier.isPrivate(modifiers)); + if (!clazz.isMemberClass() || !Modifier.isStatic(modifiers)) { + ALog.e(TAG, String.format("为了防止内存泄漏,请使用静态的成员类(public static class %s)或文件类(%s.java)", + clazz.getSimpleName(), clazz.getSimpleName())); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java index 94776ad1..93168562 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/DbDataHelper.java @@ -15,12 +15,13 @@ */ package com.arialyy.aria.util; -import com.arialyy.aria.core.wrapper.RecordWrapper; import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.download.DGEntityWrapper; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.orm.DbEntity; import java.io.File; import java.util.ArrayList; @@ -35,15 +36,20 @@ public class DbDataHelper { * 获取任务记录 * * @param filePath 文件地址 + * @param taskType 任务类型{@link ITaskWrapper} * @return 没有记录返回null,有记录则返回任务记录 */ - public static TaskRecord getTaskRecord(String filePath) { - List record = - DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", filePath); - if (record == null || record.size() == 0) { - return null; + public static TaskRecord getTaskRecord(String filePath, int taskType) { + TaskRecord taskRecord = + DbEntity.findFirst(TaskRecord.class, "filePath=? AND taskType=?", filePath, + String.valueOf(taskType)); + if (taskRecord != null) { + taskRecord.threadRecords = + DbEntity.findDatas(ThreadRecord.class, "taskKey=? AND threadType=?", filePath, + String.valueOf(taskType)); } - return record.get(0).taskRecord; + + return taskRecord; } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index dfc7ff7a..08603eb2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -25,6 +25,7 @@ import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.M3U8Entity; import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.wrapper.RecordWrapper; import com.arialyy.aria.orm.DbEntity; import java.io.File; @@ -187,7 +188,7 @@ public class RecordUtil { * @return true 为m3u8任务 */ private static boolean recordIsM3U8(int recordType) { - return recordType == TaskRecord.TYPE_M3U8_VOD || recordType == TaskRecord.TYPE_M3U8_LIVE; + return recordType == ITaskWrapper.M3U8_VOD || recordType == ITaskWrapper.M3U8_LIVE; } /** @@ -361,13 +362,14 @@ public class RecordUtil { * * @param oldPath 旧的文件路径 * @param newPath 新的文件路径 + * @param taskType 任务类型{@link ITaskWrapper} */ - public static void modifyTaskRecord(String oldPath, String newPath) { + public static void modifyTaskRecord(String oldPath, String newPath, int taskType) { if (oldPath.equals(newPath)) { ALog.w(TAG, "修改任务记录失败,新文件路径和旧文件路径一致"); return; } - TaskRecord record = DbDataHelper.getTaskRecord(oldPath); + TaskRecord record = DbDataHelper.getTaskRecord(oldPath, taskType); if (record == null) { if (new File(oldPath).exists()) { ALog.w(TAG, "修改任务记录失败,文件【" + oldPath + "】对应的任务记录不存在"); diff --git a/README.md b/README.md index 05c0779b..16f18028 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.7.10-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.10-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.10-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.10-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.8-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.8-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.10' -annotationProcessor 'com.arialyy.aria:compiler:3.7.10' -implementation 'com.arialyy.aria:ftpComponent:3.7.10' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.10' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.8' +annotationProcessor 'com.arialyy.aria:compiler:3.8' +implementation 'com.arialyy.aria:ftpComponent:3.8' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.8' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -65,7 +65,7 @@ api('com.arialyy.aria:core:'){ __⚠️注意:3.5.4以下版本升级时,需要更新[配置文件](https://aria.laoyuyu.me/aria_doc/start/config.html)!!__ -__⚠️注意:3.7 以上版本已经适配了AndroidX,如果是使用support库的,可使用[老版本](https://github.com/AriaLyy/Aria/tree/v3.6.6)__ +__⚠️注意:3.8 以上版本已经适配了AndroidX和support库都可以使用 *** ## 使用 @@ -137,13 +137,19 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.10 (2019/12/3) - - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 - - fix bug https://github.com/AriaLyy/Aria/issues/542 - - fix bug https://github.com/AriaLyy/Aria/issues/547 - - 修复下载失败时,中断重试无效的问题 - - 增加忽略权限检查的api,`ignoreCheckPermissions()` - - 增加通用的的忽略文件路径被占用的api,`isIgnoreFilePathOccupy()` + + v_3.8 (2019/12/17) + - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 + - 修复一个在xml中使用fragment导致的内存泄漏问题 + - m3u8协议的key信息增加了`keyFormat`,`keyFormatVersion`字段 + - m3u8增加了`ignoreFailureTs`方法,忽略下载失败的ts切片 + - 修复在dialogFragment的`onCreateDialog()`注册导致的注解不生效问题 + - 修复组合任务初始化失败时,无法删除的问题 + - 修复`reStart()`后,无法停止的问题 + - ftp增加主动模式,开启主动模式:https://aria.laoyuyu.me/aria_doc/api/ftp_params.html + - 修复ftp服务器无法响应`abor`命令导致的无法停止上传的问题 https://github.com/AriaLyy/Aria/issues/564 + - 修复ftp上传时,服务器有长度为0的文件导致上传失败的问题 + - 修复下载任务和上传任务的文件路径是同一个时,导致的记录混乱问题 + - 优化提示 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java index 2fb5db4e..ef2c856b 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/FtpUploadActivity.java @@ -55,7 +55,8 @@ public class FtpUploadActivity extends BaseActivity { private String mUrl; private UploadModule mModule; private long mTaskId = -1; - private String user = "lao", pwd = "123456"; + private String user = "ftpuser", pwd = "ftpuser2020"; + //private String user = "lao", pwd = "123456"; @Override protected void init(Bundle savedInstanceState) { setTile("D_FTP 文件上传"); @@ -92,11 +93,11 @@ public class FtpUploadActivity extends BaseActivity { } private void setHelpCode() { - try { - getBinding().codeView.setSource(AppUtil.getHelpCode(this, "FtpUpload.java")); - } catch (IOException e) { - e.printStackTrace(); - } + //try { + // getBinding().codeView.setSource(AppUtil.getHelpCode(this, "FtpUpload.java")); + //} catch (IOException e) { + // e.printStackTrace(); + //} } @Override protected int setLayoutId() { @@ -245,7 +246,7 @@ public class FtpUploadActivity extends BaseActivity { @Override public FtpInterceptHandler onIntercept(UploadEntity entity, List fileList) { FtpInterceptHandler.Builder builder = new FtpInterceptHandler.Builder(); //builder.coverServerFile(); // 覆盖远端同名文件 - builder.resetFileName("test.zip"); //修改上传到远端服务器的文件名 + builder.resetFileName("test12.zip"); //修改上传到远端服务器的文件名 return builder.build(); } } diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index 59365c1b..6dcb68d7 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -35,9 +35,12 @@ public class UploadModule extends BaseViewModule { * 获取Ftp上传信息 */ LiveData getFtpInfo(Context context) { - String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aab/你好"); - String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, - Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db"); + //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aab/你好"); + //String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, + // Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db"); + String url = "ftp://101.132.33.64:21/home/ftpuser/videopic/historymonitor/jobs/test"; + //String url = "ftp://9.9.9.72:2121/aab/你好"; + String filePath = "/mnt/sdcard/QQMusic-import-1.2.1.zip"; UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath); if (entity != null) { diff --git a/app/src/main/res/layout/activity_ftp_upload.xml b/app/src/main/res/layout/activity_ftp_upload.xml index 174e9ab4..e71cacb0 100644 --- a/app/src/main/res/layout/activity_ftp_upload.xml +++ b/app/src/main/res/layout/activity_ftp_upload.xml @@ -72,11 +72,11 @@ /> - + + + + + diff --git a/build.gradle b/build.gradle index 42116f03..c5910ecb 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ task clean(type: Delete) { ext { versionCode = 380 - versionName = '3.8_pre_1' + versionName = '3.8' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 8dc16ce302d824313b1fa661132ed3832910a2a9 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sun, 22 Dec 2019 11:30:29 +0800 Subject: [PATCH 038/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=A1=A8=E5=88=9B=E5=BB=BA=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20https://github.com/AriaLyy/Aria/issues/570=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA=E9=9D=9E=E5=88=86=E5=9D=97?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E5=AF=BC=E8=87=B4=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98=20https://github.?= =?UTF-8?q?com/AriaLyy/Aria/issues/571=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=AB=AF=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=88=9B=E5=BB=BAsocket=E8=BF=9E=E6=8E=A5=EF=BC=8C=E5=8D=B4?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=BF=94=E5=9B=9E=E7=A0=81=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=8D=A1=E4=BD=8F=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20https://github.com/AriaLyy/Aria/issues/569=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6=E5=88=A0=E9=99=A4=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E7=BB=84=E5=90=88=E4=BB=BB=E5=8A=A1=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E4=B8=8B=E8=BD=BD=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20https://github.com/AriaLyy/Aria/issues/574=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=BC=93=E5=AD=98=E9=98=9F=E5=88=97=E5=92=8C=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E9=98=9F=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppFrame/build.gradle | 2 +- .../arialyy/aria/core/queue/AbsTaskQueue.java | 12 +- .../arialyy/aria/core/queue/DTaskQueue.java | 47 ++- .../aria/core/queue/pool/BaseCachePool.java | 77 ++--- .../aria/core/queue/pool/BaseExecutePool.java | 91 ++---- .../core/queue/pool/DGLoadExecutePool.java | 3 +- .../core/queue/pool/DLoadExecutePool.java | 31 +- DEV_LOG.md | 8 +- .../apache/commons/net/ftp/FTPClient.java | 4 - .../aria/ftp/BaseFtpThreadTaskAdapter.java | 3 +- .../aria/ftp/upload/FtpUFileInfoThread.java | 10 +- .../ftp/upload/FtpUThreadTaskAdapter.java | 51 +++- .../arialyy/aria/http/HttpFileInfoThread.java | 5 + .../aria/http/download/DGroupLoaderUtil.java | 1 + .../http/download/HttpDThreadTaskAdapter.java | 1 + .../aria/core/common/RecordHelper.java | 26 +- .../arialyy/aria/core/group/AbsGroupUtil.java | 13 +- .../aria/core/listener/BaseListener.java | 5 +- .../aria/core/manager/ThreadTaskManager.java | 17 +- .../com/arialyy/aria/core/task/AbsTask.java | 2 +- .../aria/core/wrapper/ITaskWrapper.java | 5 + .../com/arialyy/aria/orm/AbsDelegate.java | 55 +--- .../com/arialyy/aria/orm/DelegateCommon.java | 242 --------------- .../com/arialyy/aria/orm/DelegateFind.java | 4 + .../com/arialyy/aria/orm/DelegateUpdate.java | 8 +- .../com/arialyy/aria/orm/DelegateWrapper.java | 7 +- .../java/com/arialyy/aria/orm/SqlHelper.java | 35 +-- .../java/com/arialyy/aria/orm/SqlUtil.java | 283 +++++++++++++++++- .../com/arialyy/aria/util/ComponentUtil.java | 1 + README.md | 35 +-- app/src/main/assets/aria_config.xml | 2 +- app/src/main/assets/aria_config_1.xml | 161 ---------- .../java/com/arialyy/simple/MainActivity.java | 2 + .../download/group/DownloadGroupActivity.java | 2 +- .../arialyy/simple/modlue/CommonModule.java | 3 +- app/src/main/res/drawable/ic_sftp.xml | 15 + app/src/main/res/values/strings.xml | 8 +- build.gradle | 4 +- 38 files changed, 563 insertions(+), 718 deletions(-) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java delete mode 100644 app/src/main/assets/aria_config_1.xml create mode 100644 app/src/main/res/drawable/ic_sftp.xml diff --git a/AppFrame/build.gradle b/AppFrame/build.gradle index 2dc8b08a..f95f1802 100644 --- a/AppFrame/build.gradle +++ b/AppFrame/build.gradle @@ -35,7 +35,7 @@ dependencies { api 'com.google.code.gson:gson:2.8.2' api 'io.reactivex:rxandroid:1.2.0' api 'io.reactivex:rxjava:1.1.5' - implementation 'com.squareup.okhttp3:okhttp:3.2.0' + implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.retrofit2:retrofit:2.1.0' implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0' implementation 'com.squareup.retrofit2:converter-gson:2.1.0' diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index ea3c768a..eb5f9c99 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -95,8 +95,7 @@ public abstract class AbsTaskQueue { */ public void setTaskHighestPriority(DownloadTask task) { task.setHighestPriority(true); - Map exeTasks = mExecutePool.getAllTask(); + //Map exeTasks = mExecutePool.getAllTask(); + List exeTasks = mExecutePool.getAllTask(); if (exeTasks != null && !exeTasks.isEmpty()) { - Set keys = exeTasks.keySet(); - for (String key : keys) { - DownloadTask temp = exeTasks.get(key); + for (DownloadTask temp : exeTasks) { if (temp != null && temp.isRunning() && temp.isHighestPriorityTask() && !temp.getKey() .equals(task.getKey())) { ALog.e(TAG, "设置最高优先级任务失败,失败原因【任务中已经有最高优先级任务,请等待上一个最高优先级任务完成,或手动暂停该任务】"); @@ -80,28 +79,28 @@ public class DTaskQueue extends AbsTaskQueue { return; } } - } - int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum(); - int currentSize = mExecutePool.size(); - if (currentSize == 0 || currentSize < maxSize) { - startTask(task); - } else { - Set tempTasks = new LinkedHashSet<>(); - for (int i = 0; i < maxSize; i++) { - DownloadTask oldTsk = mExecutePool.pollTask(); - if (oldTsk != null && oldTsk.isRunning()) { - if (i == maxSize - 1) { - oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT); - mCachePool.putTaskToFirst(oldTsk); - break; + int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum(); + int currentSize = mExecutePool.size(); + if (currentSize == 0 || currentSize < maxSize) { + startTask(task); + } else { + Set tempTasks = new LinkedHashSet<>(); + for (int i = 0; i < maxSize; i++) { + DownloadTask oldTsk = mExecutePool.pollTask(); + if (oldTsk != null && oldTsk.isRunning()) { + if (i == maxSize - 1) { + oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT); + mCachePool.putTaskToFirst(oldTsk); + break; + } + tempTasks.add(oldTsk); } - tempTasks.add(oldTsk); } - } - startTask(task); + startTask(task); - for (DownloadTask temp : tempTasks) { - mExecutePool.putTask(temp); + for (DownloadTask temp : tempTasks) { + mExecutePool.putTask(temp); + } } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java index 2fd7f409..89a442fc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseCachePool.java @@ -20,65 +20,43 @@ import android.text.TextUtils; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.concurrent.LinkedBlockingDeque; /** * Created by lyy on 2016/8/14. 任务缓存池,所有下载任务最先缓存在这个池中 */ public class BaseCachePool implements IPool { - private static final String TAG = "BaseCachePool"; + private final String TAG = CommonUtil.getClassName(this); private static final int MAX_NUM = Integer.MAX_VALUE; //最大下载任务数 - private static final long TIME_OUT = 1000; private static final Object LOCK = new Object(); - private Map mCacheMap; - private LinkedBlockingQueue mCacheQueue; + private Deque mCacheQueue; BaseCachePool() { - mCacheQueue = new LinkedBlockingQueue<>(MAX_NUM); - mCacheMap = new ConcurrentHashMap<>(); + mCacheQueue = new LinkedBlockingDeque<>(MAX_NUM); } /** * 获取被缓存的任务 */ - public Map getAllTask() { - return mCacheMap; + public List getAllTask() { + return new ArrayList<>(mCacheQueue); } /** * 清除所有缓存的任务 */ public void clear() { - for (String key : mCacheMap.keySet()) { - TASK task = mCacheMap.get(key); - mCacheQueue.remove(task); - mCacheMap.remove(key); - } + mCacheQueue.clear(); } /** * 将任务放在队首 */ public boolean putTaskToFirst(TASK task) { - if (mCacheQueue.isEmpty()) { - return putTask(task); - } else { - Set temps = new LinkedHashSet<>(); - temps.add(task); - for (int i = 0, len = size(); i < len; i++) { - TASK temp = pollTask(); - temps.add(temp); - } - for (TASK t : temps) { - putTask(t); - } - return true; - } + return mCacheQueue.offerFirst(task); } @Override public boolean putTask(TASK task) { @@ -87,16 +65,12 @@ public class BaseCachePool implements IPool { ALog.e(TAG, "任务不能为空!!"); return false; } - String key = task.getKey(); if (mCacheQueue.contains(task)) { ALog.w(TAG, "任务【" + task.getTaskName() + "】进入缓存队列失败,原因:已经在缓存队列中"); return false; } else { boolean s = mCacheQueue.offer(task); ALog.d(TAG, "任务【" + task.getTaskName() + "】进入缓存队列" + (s ? "成功" : "失败")); - if (s) { - mCacheMap.put(CommonUtil.keyToHashKey(key), task); - } return s; } } @@ -104,19 +78,8 @@ public class BaseCachePool implements IPool { @Override public TASK pollTask() { synchronized (LOCK) { - try { - TASK task; - task = mCacheQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); - if (task != null) { - String url = task.getKey(); - mCacheMap.remove(CommonUtil.keyToHashKey(url)); - } - return task; - } catch (InterruptedException e) { - e.printStackTrace(); - } + return mCacheQueue.pollFirst(); } - return null; } @Override public TASK getTask(String key) { @@ -125,12 +88,17 @@ public class BaseCachePool implements IPool { ALog.e(TAG, "key 为null"); return null; } - return mCacheMap.get(CommonUtil.keyToHashKey(key)); + for (TASK task : mCacheQueue) { + if (task.getKey().equals(key)) { + return task; + } + } } + return null; } @Override public boolean taskExits(String key) { - return mCacheMap.containsKey(CommonUtil.keyToHashKey(key)); + return getTask(key) != null; } @Override public boolean removeTask(TASK task) { @@ -139,8 +107,6 @@ public class BaseCachePool implements IPool { ALog.e(TAG, "任务不能为空"); return false; } else { - String key = CommonUtil.keyToHashKey(task.getKey()); - mCacheMap.remove(key); return mCacheQueue.remove(task); } } @@ -152,10 +118,7 @@ public class BaseCachePool implements IPool { ALog.e(TAG, "请传入有效的下载链接"); return false; } - String temp = CommonUtil.keyToHashKey(key); - TASK task = mCacheMap.get(temp); - mCacheMap.remove(temp); - return mCacheQueue.remove(task); + return mCacheQueue.remove(getTask(key)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java index 4ae49c99..533105ee 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/BaseExecutePool.java @@ -21,26 +21,23 @@ import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; -import java.util.Map; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.concurrent.LinkedBlockingDeque; /** * Created by lyy on 2016/8/15. 任务执行池,所有当前下载任务都该任务池中,默认下载大小为2 */ public class BaseExecutePool implements IPool { - private final String TAG = "BaseExecutePool"; + private final String TAG = CommonUtil.getClassName(this); private static final Object LOCK = new Object(); - final long TIME_OUT = 1000; - ArrayBlockingQueue mExecuteQueue; - Map mExecuteMap; + Deque mExecuteQueue; int mSize; BaseExecutePool() { mSize = getMaxSize(); - mExecuteQueue = new ArrayBlockingQueue<>(mSize); - mExecuteMap = new ConcurrentHashMap<>(); + mExecuteQueue = new LinkedBlockingDeque<>(mSize); } /** @@ -55,8 +52,8 @@ public class BaseExecutePool implements IPool { /** * 获取所有正在执行的任务 */ - public Map getAllTask() { - return mExecuteMap; + public List getAllTask() { + return new ArrayList<>(mExecuteQueue); } @Override public boolean putTask(TASK task) { @@ -88,17 +85,13 @@ public class BaseExecutePool implements IPool { */ public void setMaxNum(int maxNum) { synchronized (LOCK) { - try { - ArrayBlockingQueue temp = new ArrayBlockingQueue<>(maxNum); - TASK task; - while ((task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS)) != null) { - temp.offer(task); - } - mExecuteQueue = temp; - mSize = maxNum; - } catch (InterruptedException e) { - e.printStackTrace(); + Deque temp = new LinkedBlockingDeque<>(maxNum); + TASK task; + while ((task = mExecuteQueue.poll()) != null) { + temp.offer(task); } + mExecuteQueue = temp; + mSize = maxNum; } } @@ -109,12 +102,8 @@ public class BaseExecutePool implements IPool { */ boolean putNewTask(TASK newTask) { synchronized (LOCK) { - String url = newTask.getKey(); boolean s = mExecuteQueue.offer(newTask); ALog.d(TAG, "任务【" + newTask.getTaskName() + "】进入执行队列" + (s ? "成功" : "失败")); - if (s) { - mExecuteMap.put(CommonUtil.keyToHashKey(url), newTask); - } return s; } } @@ -124,37 +113,19 @@ public class BaseExecutePool implements IPool { */ boolean pollFirstTask() { synchronized (LOCK) { - try { - TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); - if (oldTask == null) { - ALog.w(TAG, "移除任务失败,原因:任务为null"); - return false; - } - oldTask.stop(); - String key = CommonUtil.keyToHashKey(oldTask.getKey()); - mExecuteMap.remove(key); - } catch (InterruptedException e) { - e.printStackTrace(); + TASK oldTask = mExecuteQueue.pollFirst(); + if (oldTask == null) { + ALog.w(TAG, "移除任务失败,原因:任务为null"); return false; } + oldTask.stop(); return true; } } @Override public TASK pollTask() { synchronized (LOCK) { - try { - TASK task; - task = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); - if (task != null) { - String url = task.getKey(); - mExecuteMap.remove(CommonUtil.keyToHashKey(url)); - } - return task; - } catch (InterruptedException e) { - e.printStackTrace(); - } - return null; + return mExecuteQueue.poll(); } } @@ -164,12 +135,18 @@ public class BaseExecutePool implements IPool { ALog.e(TAG, "key为null"); return null; } - return mExecuteMap.get(CommonUtil.keyToHashKey(key)); + for (TASK task : mExecuteQueue) { + if (task.getKey().equals(key)) { + return task; + } + } + + return null; } } @Override public boolean taskExits(String key) { - return mExecuteMap.containsKey(CommonUtil.keyToHashKey(key)); + return getTask(key) != null; } @Override public boolean removeTask(TASK task) { @@ -189,16 +166,8 @@ public class BaseExecutePool implements IPool { ALog.e(TAG, "key 为null"); return false; } - String convertKey = CommonUtil.keyToHashKey(key); - TASK task = mExecuteMap.get(convertKey); - final int oldQueueSize = mExecuteQueue.size(); - boolean isSuccess = mExecuteQueue.remove(task); - final int newQueueSize = mExecuteQueue.size(); - if (isSuccess && newQueueSize != oldQueueSize) { - mExecuteMap.remove(convertKey); - return true; - } - return false; + + return mExecuteQueue.remove(getTask(key)); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java index 099c1002..ee3ea11e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DGLoadExecutePool.java @@ -18,13 +18,14 @@ package com.arialyy.aria.core.queue.pool; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.task.AbsTask; +import com.arialyy.aria.util.CommonUtil; /** * Created by AriaL on 2017/6/29. * 单个下载任务的执行池 */ class DGLoadExecutePool extends DLoadExecutePool { - private final String TAG = "DGLoadExecutePool"; + private final String TAG = CommonUtil.getClassName(this); @Override protected int getMaxSize() { return AriaConfig.getInstance().getDGConfig().getMaxTaskNum(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java index d8db105f..bfa8afd3 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/pool/DLoadExecutePool.java @@ -18,9 +18,6 @@ package com.arialyy.aria.core.queue.pool; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import java.util.Set; -import java.util.concurrent.TimeUnit; /** * Created by AriaL on 2017/6/29. @@ -45,9 +42,10 @@ class DLoadExecutePool extends BaseExecutePool { return false; } else { if (mExecuteQueue.size() >= mSize) { - Set keys = mExecuteMap.keySet(); - for (String key : keys) { - if (mExecuteMap.get(key).isHighestPriorityTask()) return false; + for (TASK temp : mExecuteQueue) { + if (temp.isHighestPriorityTask()) { + return false; + } } if (pollFirstTask()) { return putNewTask(task); @@ -61,22 +59,15 @@ class DLoadExecutePool extends BaseExecutePool { } @Override boolean pollFirstTask() { - try { - TASK oldTask = mExecuteQueue.poll(TIME_OUT, TimeUnit.MICROSECONDS); - if (oldTask == null) { - ALog.w(TAG, "移除任务失败,错误原因:任务为null"); - return false; - } - if (oldTask.isHighestPriorityTask()) { - return false; - } - oldTask.stop(); - String key = CommonUtil.keyToHashKey(oldTask.getKey()); - mExecuteMap.remove(key); - } catch (InterruptedException e) { - e.printStackTrace(); + TASK oldTask = mExecuteQueue.pollFirst(); + if (oldTask == null) { + ALog.w(TAG, "移除任务失败,错误原因:任务为null"); + return false; + } + if (oldTask.isHighestPriorityTask()) { return false; } + oldTask.stop(); return true; } } diff --git a/DEV_LOG.md b/DEV_LOG.md index bd29e876..2d90f30c 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -1,4 +1,10 @@ ## 开发日志 + + v_3.8.1 (2019/12/22) + - 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570 + - 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 + - 修复一个服务器端无法创建socket连接,却没有返回码导致客户端卡住的问题 https://github.com/AriaLyy/Aria/issues/569 + - 修复文件删除后,组合任务没有重新下载的问题 https://github.com/AriaLyy/Aria/issues/574 + - 优化缓存队列和执行队列 + v_3.8 (2019/12/17) - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 - 修复一个在xml中使用fragment导致的内存泄漏问题 @@ -15,7 +21,7 @@ + v_3.7.10 (2019/12/3) - fix bug https://github.com/AriaLyy/Aria/issues/543#issuecomment-559733124 - fix bug https://github.com/AriaLyy/Aria/issues/542 - - fix bug https://github.com/AriaLyy/Aria/issues/547 + - fix bug https://github.com/AriaLyy/Azria/issues/547 - 修复下载失败时,中断重试无效的问题 - 增加忽略权限检查的api,`ignoreCheckPermissions()` - 增加通用的的忽略文件路径被占用的api,`isIgnoreFilePathOccupy()` diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java index 07e9cae3..5b85dcc7 100644 --- a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPClient.java @@ -3662,7 +3662,6 @@ public class FTPClient extends FTP implements Configurable { private long time = System.currentTimeMillis(); private int notAcked; private OnFtpInputStreamListener listener; - private long lTime = time; CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException { this(parent, idleTime, maxWait, null); @@ -3696,12 +3695,9 @@ public class FTPClient extends FTP implements Configurable { time = now; } - //if (now - lTime > 100) { if (listener != null) { listener.onFtpInputStream(parent, totalBytesTransferred, bytesTransferred, streamSize); } - //lTime = now; - //} } void cleanUp() throws IOException { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java index f3c604f3..4d029c3a 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/BaseFtpThreadTaskAdapter.java @@ -117,7 +117,8 @@ public abstract class BaseFtpThreadTaskAdapter extends AbsThreadTaskAdapter { } } client.setControlEncoding(charSet); - client.setDataTimeout(getTaskConfig().getIOTimeOut()); + //client.setDataTimeout(getTaskConfig().getIOTimeOut()); + client.setDataTimeout(1000); client.setConnectTimeout(getTaskConfig().getConnectTimeOut()); if (mTaskOption.getConnMode() == FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY) { client.enterLocalActiveMode(); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java index af113b53..9ffc376e 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoThread.java @@ -42,10 +42,6 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { static final int CODE_COMPLETE = 0xab1; private boolean isComplete = false; private String remotePath; - /** - * true 使用拦截器,false 不使用拦截器 - */ - private boolean useInterceptor = false; FtpUFileInfoThread(UTaskWrapper taskEntity, OnFileInfoCallback callback) { super(taskEntity, callback); @@ -65,7 +61,9 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { try { IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor(); if (interceptor != null) { - useInterceptor = true; + /** + * true 使用拦截器,false 不使用拦截器 + */ List files = new ArrayList<>(); for (FTPFile ftpFile : ftpFiles) { if (ftpFile.isDirectory()) { @@ -118,7 +116,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread { */ @Override protected void handleFile(String remotePath, FTPFile ftpFile) { super.handleFile(remotePath, ftpFile); - if (ftpFile != null && !useInterceptor) { + if (ftpFile != null) { //远程文件已完成 if (ftpFile.getSize() == mEntity.getFileSize()) { isComplete = true; diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index bd5e4bb7..c8da304c 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -28,6 +28,8 @@ import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.CommonUtil; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * Created by Aria.Lao on 2017/7/28. D_FTP 单线程上传任务,需要FTP 服务器给用户打开append和write的权限 @@ -35,13 +37,16 @@ import java.io.UnsupportedEncodingException; class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private String dir, remotePath; private boolean storeFail = false; + private ScheduledThreadPoolExecutor timer; + private FTPClient client = null; + private boolean isTimeOut = true; + private FtpFISAdapter fa; FtpUThreadTaskAdapter(SubThreadConfig config) { super(config); } @Override protected void handlerThreadTask() { - FTPClient client = null; BufferedRandomAccessFile file = null; try { ALog.d(TAG, @@ -71,7 +76,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (getThreadRecord().startLocation > 0) { file.seek(getThreadRecord().startLocation); } - boolean complete = upload(client, file); + boolean complete = upload(file); if (!complete || getThreadTask().isBreak()) { return; } @@ -94,6 +99,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { e.printStackTrace(); } closeClient(client); + closeTimer(); } } @@ -112,15 +118,46 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { String.format("%s/%s", mTaskOption.getUrlEntity().remotePath, fileName)); } + /** + * 启动监听定时器,当网络断开时,如果该任务的FTP服务器的传输线程没有断开,当客户端重新连接时,客户端将无法发送数据到服务端 + * 每隔10s检查一次。 + */ + private void startTimer() { + timer = new ScheduledThreadPoolExecutor(1); + timer.scheduleWithFixedDelay(new Runnable() { + @Override public void run() { + try { + if (isTimeOut) { + fail(new AriaIOException(TAG, "socket连接失败,该问题一般出现于网络断开,客户端重新连接," + + "但是服务器端无法创建socket缺没有返回错误码的情况。"), false); + } + if (fa != null) { + fa.close(); + } + isTimeOut = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + }, 10, 10, TimeUnit.SECONDS); + } + + private void closeTimer() { + if (timer != null && !timer.isShutdown()) { + timer.shutdown(); + } + } + /** * 上传 * * @return {@code true}上传成功、{@code false} 上传失败 */ - private boolean upload(final FTPClient client, final BufferedRandomAccessFile bis) + private boolean upload(final BufferedRandomAccessFile bis) throws IOException { - final FtpFISAdapter fa = new FtpFISAdapter(bis); + fa = new FtpFISAdapter(bis); storeFail = false; + startTimer(); try { ALog.d(TAG, String.format("remotePath: %s", remotePath)); client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { @@ -129,6 +166,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, int bytesTransferred, long streamSize) { try { + isTimeOut = false; if (getThreadTask().isBreak() && !isStoped) { isStoped = true; client.abor(); @@ -150,9 +188,10 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } }); } catch (IOException e) { - String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReplyCode(), + String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReply(), client.getReplyString(), getEntity().getFilePath()); closeClient(client); + e.printStackTrace(); if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); } else { @@ -163,7 +202,7 @@ class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (storeFail) { return false; } - int reply = client.getReplyCode(); + int reply = client.getReply(); if (!FTPReply.isPositiveCompletion(reply)) { if (reply != FTPReply.TRANSFER_ABORTED) { fail(new AriaIOException(TAG, diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java index 9c7103e0..e484c5cc 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java @@ -89,6 +89,11 @@ public class HttpFileInfoThread implements Runnable { true); } finally { if (conn != null) { + try { + conn.getInputStream().close(); + } catch (IOException e) { + e.printStackTrace(); + } conn.disconnect(); } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index fc66b55a..016576c7 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -76,6 +76,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil { getState().setSubSize(getWrapper().getSubTaskWrapper().size()); if (getState().getCompleteNum() == getState().getSubSize()) { mListener.onComplete(); + return false; } else { // 处理组合任务大小未知的情况 if (getWrapper().isUnknownSize() && getWrapper().getEntity().getFileSize() < 1) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java index 45ea172a..1ae4cdee 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java @@ -143,6 +143,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter { is.close(); } if (conn != null) { + conn.getInputStream().close(); conn.disconnect(); } } catch (IOException e) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 12c2dd3d..2975dc29 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -51,15 +51,12 @@ public class RecordHelper { File temp = new File(mTaskRecord.filePath); boolean fileExists = false; if (!temp.exists()) { - BufferedRandomAccessFile tempFile; - try { - tempFile = new BufferedRandomAccessFile(temp, "rw"); - tempFile.setLength(mWrapper.getEntity().getFileSize()); - } catch (IOException e) { - e.printStackTrace(); - } - + createPlaceHolderFile(temp); } else { + if (temp.length() != mWrapper.getEntity().getFileSize()) { + FileUtil.deleteFile(temp); + createPlaceHolderFile(temp); + } fileExists = true; } // 处理文件被删除的情况 @@ -81,6 +78,19 @@ public class RecordHelper { mWrapper.setNewTask(false); } + /** + * 创建非分块的占位文件 + */ + private void createPlaceHolderFile(File temp) { + BufferedRandomAccessFile tempFile; + try { + tempFile = new BufferedRandomAccessFile(temp, "rw"); + tempFile.setLength(mWrapper.getEntity().getFileSize()); + } catch (IOException e) { + e.printStackTrace(); + } + } + /** * 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index 12e8bdce..b216b003 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -27,6 +27,7 @@ import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; +import java.io.File; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -83,14 +84,24 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { private void initState() { mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue); for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { - if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) { + File subFile = new File(wrapper.getEntity().getFilePath()); + if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE + && subFile.exists() + && subFile.length() == wrapper.getEntity().getFileSize()) { mState.updateCompleteNum(); mCurrentLocation += wrapper.getEntity().getFileSize(); } else { + if (!subFile.exists()) { + wrapper.getEntity().setCurrentProgress(0); + } + wrapper.getEntity().setState(IEntity.STATE_POST_PRE); mCache.put(wrapper.getKey(), wrapper); mCurrentLocation += wrapper.getEntity().getCurrentProgress(); } } + if (getWrapper().getSubTaskWrapper().size() != mState.getCompleteNum()) { + getWrapper().setState(IEntity.STATE_POST_PRE); + } mState.updateProgress(mCurrentLocation); mScheduler = new Handler(Looper.getMainLooper(), SimpleSchedulers.newInstance(mState)); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index c36be997..2dc40426 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -122,8 +122,9 @@ public abstract class BaseListener> mThreadTasks = new ConcurrentHashMap<>(); + private static final int CORE_POOL_NUM = 20; private static final ReentrantLock LOCK = new ReentrantLock(); + private ThreadPoolExecutor mExePool; + private Map> mThreadTasks = new ConcurrentHashMap<>(); public static synchronized ThreadTaskManager getInstance() { if (INSTANCE == null) { @@ -48,7 +50,10 @@ public class ThreadTaskManager { } private ThreadTaskManager() { - mExePool = Executors.newCachedThreadPool(); + mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + mExePool.allowsCoreThreadTimeOut(); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java index 503c0869..46cefc0d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java @@ -79,7 +79,7 @@ public abstract class AbsTask ALog.e(TAG, "key 为空"); return; } else if (obj == null) { - ALog.w(TAG, "扩展数据为空"); + ALog.i(TAG, "扩展数据为空"); return; } mExpand.put(key, obj); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java index 047e6112..7f3acdc8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java @@ -79,6 +79,11 @@ public interface ITaskWrapper { */ int U_TCP_PEER = 11; + /** + * SFTP 下载 + */ + int D_SFTP = 12; + /** * 获取任务类型 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java index 132139f3..499e81bb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/AbsDelegate.java @@ -17,10 +17,6 @@ package com.arialyy.aria.orm; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import java.lang.reflect.Field; -import java.net.URLEncoder; /** * Created by laoyuyu on 2018/3/22. @@ -28,52 +24,8 @@ import java.net.URLEncoder; abstract class AbsDelegate { static final String TAG = "AbsDelegate"; - - - /** - * 检查list参数是否合法,list只能是{@code List} - * - * @return {@code true} 合法 - */ - boolean checkList(Field list) { - Class t = CommonUtil.getListParamType(list); - if (t != null && t == String.class) { - return true; - } else { - ALog.d(TAG, "map参数错误,支持List的参数字段"); - return false; - } - } - - /** - * 检查map参数是否合法,map只能是{@code Map} - * - * @return {@code true} 合法 - */ - boolean checkMap(Field map) { - Class[] ts = CommonUtil.getMapParamType(map); - if (ts != null - && ts[0] != null - && ts[1] != null - && ts[0] == String.class - && ts[1] == String.class) { - return true; - } else { - ALog.d(TAG, "map参数错误,支持Map的参数字段"); - return false; - } - } - void closeCursor(Cursor cursor) { - synchronized (AbsDelegate.class) { - if (cursor != null && !cursor.isClosed()) { - try { - cursor.close(); - } catch (android.database.SQLException e) { - e.printStackTrace(); - } - } - } + SqlUtil.closeCursor(cursor); } /** @@ -82,9 +34,6 @@ abstract class AbsDelegate { * @return 返回数据库 */ SQLiteDatabase checkDb(SQLiteDatabase db) { - if (db == null || !db.isOpen()) { - db = SqlHelper.getInstance().getDb(); - } - return db; + return SqlUtil.checkDb(db); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java deleted file mode 100644 index d0205bf8..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateCommon.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.orm; - -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.text.TextUtils; -import com.arialyy.aria.orm.annotation.Default; -import com.arialyy.aria.orm.annotation.Foreign; -import com.arialyy.aria.orm.annotation.Primary; -import com.arialyy.aria.util.CommonUtil; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Created by laoyuyu on 2018/3/22. - * 通用委托,创建表,检查字段 - */ -class DelegateCommon extends AbsDelegate { - private DelegateCommon() { - } - - /** - * 删除指定的表 - */ - void dropTable(SQLiteDatabase db, String tableName) { - db = checkDb(db); - String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName); - //db.beginTransaction(); - db.execSQL(deleteSQL); - //db.setTransactionSuccessful(); - //db.endTransaction(); - } - - /** - * 清空表数据 - */ - void clean(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - String tableName = CommonUtil.getClassName(clazz); - if (tableExists(db, clazz)) { - String sql = "DELETE FROM " + tableName; - db.execSQL(sql); - } - } - - /** - * 查找表是否存在 - * - * @param clazz 数据库实体 - * @return true,该数据库实体对应的表存在;false,不存在 - */ - boolean tableExists(SQLiteDatabase db, Class clazz) { - return tableExists(db, CommonUtil.getClassName(clazz)); - } - - /** - * 查找表是否存在 - * - * @param tableName 表名 - * @return true,该数据库实体对应的表存在;false,不存在 - */ - boolean tableExists(SQLiteDatabase db, String tableName) { - db = checkDb(db); - Cursor cursor = null; - try { - String sql = - String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", - tableName); - cursor = db.rawQuery(sql, null); - if (cursor != null && cursor.moveToNext()) { - int count = cursor.getInt(0); - if (count > 0) { - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - closeCursor(cursor); - } - return false; - } - - /** - * 检查某个字段的值是否存在 - * - * @param expression 字段和值"url=xxx" - * @return {@code true}该字段的对应的value已存在 - */ - boolean checkDataExist(SQLiteDatabase db, Class clazz, String... expression) { - db = checkDb(db); - if (!CommonUtil.checkSqlExpression(expression)) { - return false; - } - String sql = String.format("SELECT rowid, * FROM %s WHERE %s ", CommonUtil.getClassName(clazz), - expression[0]); - sql = sql.replace("?", "%s"); - Object[] params = new String[expression.length - 1]; - for (int i = 0, len = params.length; i < len; i++) { - params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1])); - } - sql = String.format(sql, params); - Cursor cursor = db.rawQuery(sql, null); - final boolean isExist = cursor.getCount() > 0; - closeCursor(cursor); - return isExist; - } - - /** - * 创建表 - * - * @param clazz 数据库实体 - */ - void createTable(SQLiteDatabase db, Class clazz) { - db = checkDb(db); - List fields = CommonUtil.getAllFields(clazz); - if (fields != null && fields.size() > 0) { - //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 - final List foreignArray = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - sb.append("CREATE TABLE IF NOT EXISTS ") - .append(CommonUtil.getClassName(clazz)) - .append(" ("); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.isIgnore(field)) { - continue; - } - Class type = field.getType(); - sb.append(field.getName()); - if (type == String.class || type.isEnum()) { - sb.append(" VARCHAR"); - } else if (type == int.class || type == Integer.class) { - sb.append(" INTEGER"); - } else if (type == float.class || type == Float.class) { - sb.append(" FLOAT"); - } else if (type == double.class || type == Double.class) { - sb.append(" DOUBLE"); - } else if (type == long.class || type == Long.class) { - sb.append(" BIGINT"); - } else if (type == boolean.class || type == Boolean.class) { - sb.append(" BOOLEAN"); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - sb.append(" DATA"); - } else if (type == byte.class || type == Byte.class) { - sb.append(" BLOB"); - } else if (type == Map.class || type == List.class) { - sb.append(" TEXT"); - } else { - continue; - } - if (SqlUtil.isPrimary(field)) { - Primary pk = field.getAnnotation(Primary.class); - sb.append(" PRIMARY KEY"); - if (pk.autoincrement() && (type == int.class || type == Integer.class)) { - sb.append(" AUTOINCREMENT"); - } - } - - if (SqlUtil.isForeign(field)) { - foreignArray.add(field); - } - - if (SqlUtil.isNoNull(field)) { - sb.append(" NOT NULL"); - } - - if (SqlUtil.isDefault(field)) { - Default d = field.getAnnotation(Default.class); - if (!TextUtils.isEmpty(d.value())) { - sb.append(" ERROR ").append("'").append(d.value()).append("'"); - } - } - - if (SqlUtil.isUnique(field)) { - sb.append(" UNIQUE"); - } - - sb.append(","); - } - - for (Field field : foreignArray) { - Foreign foreign = field.getAnnotation(Foreign.class); - sb.append("FOREIGN KEY (") - .append(field.getName()) - .append(") REFERENCES ") - .append(CommonUtil.getClassName(foreign.parent())) - .append("(") - .append(foreign.column()) - .append(")"); - ActionPolicy update = foreign.onUpdate(); - ActionPolicy delete = foreign.onDelete(); - if (update != ActionPolicy.NO_ACTION) { - sb.append(" ON UPDATE ").append(update.function); - } - - if (delete != ActionPolicy.NO_ACTION) { - sb.append(" ON DELETE ").append(update.function); - } - sb.append(","); - } - - String str = sb.toString(); - str = str.substring(0, str.length() - 1) + ");"; - db.execSQL(str); - } - } - - /** - * 通过class 获取该class的表字段 - * - * @return 表字段列表 - */ - List getColumns(Class clazz) { - List columns = new ArrayList<>(); - List fields = CommonUtil.getAllFields(clazz); - for (Field field : fields) { - field.setAccessible(true); - if (SqlUtil.isIgnore(field)) { - continue; - } - columns.add(field.getName()); - } - return columns; - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java index 0e6a26e6..21f2af4f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -136,6 +136,9 @@ class DelegateFind extends AbsDelegate { Many m = many.getAnnotation(Many.class); Class parentClazz = Class.forName(one.getType().getName()); Class childClazz = Class.forName(CommonUtil.getListParamType(many).getName()); + // 检查表 + SqlUtil.checkTable(db, parentClazz); + SqlUtil.checkTable(db, childClazz); final String pTableName = parentClazz.getSimpleName(); final String cTableName = childClazz.getSimpleName(); List pColumn = SqlUtil.getAllNotIgnoreField(parentClazz); @@ -418,6 +421,7 @@ class DelegateFind extends AbsDelegate { */ private List exeNormalDataSql(SQLiteDatabase db, Class clazz, String sql, String[] selectionArgs) { + SqlUtil.checkTable(db, clazz); String[] temp = new String[selectionArgs.length]; int i = 0; for (String arg : selectionArgs) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java index 00e32002..4ee99afe 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateUpdate.java @@ -38,6 +38,7 @@ class DelegateUpdate extends AbsDelegate { */ synchronized void delData(SQLiteDatabase db, Class clazz, String... expression) { + SqlUtil.checkTable(db, clazz); db = checkDb(db); if (!CommonUtil.checkSqlExpression(expression)) { return; @@ -57,6 +58,7 @@ class DelegateUpdate extends AbsDelegate { * 修改某行数据 */ synchronized void updateData(SQLiteDatabase db, DbEntity dbEntity) { + SqlUtil.checkTable(db, dbEntity.getClass()); db = checkDb(db); ContentValues values = createValues(dbEntity); if (values != null) { @@ -109,6 +111,7 @@ class DelegateUpdate extends AbsDelegate { if (oldClazz == null || oldClazz != entity.getClass() || table == null) { oldClazz = entity.getClass(); table = CommonUtil.getClassName(oldClazz); + SqlUtil.checkTable(db, oldClazz); } ContentValues value = createValues(entity); @@ -130,6 +133,7 @@ class DelegateUpdate extends AbsDelegate { * 插入数据 */ synchronized void insertData(SQLiteDatabase db, DbEntity dbEntity) { + SqlUtil.checkTable(db, dbEntity.getClass()); db = checkDb(db); ContentValues values = createValues(dbEntity); if (values != null) { @@ -156,9 +160,9 @@ class DelegateUpdate extends AbsDelegate { } String value = null; Type type = field.getType(); - if (type == Map.class && checkMap(field)) { + if (type == Map.class && SqlUtil.checkMap(field)) { value = SqlUtil.map2Str((Map) field.get(dbEntity)); - } else if (type == List.class && checkList(field)) { + } else if (type == List.class && SqlUtil.checkList(field)) { value = SqlUtil.list2Str(dbEntity, field); } else { Object obj = field.get(dbEntity); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java index 53ab959d..15ae2eed 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateWrapper.java @@ -85,15 +85,14 @@ public class DelegateWrapper { * @return {@code true}该字段的对应的value已存在 */ boolean checkDataExist(Class clazz, String... expression) { - return mDManager.getDelegate(DelegateCommon.class) - .checkDataExist(mDb, clazz, expression); + return SqlUtil.checkDataExist(mDb, clazz, expression); } /** * 清空表数据 */ void clean(Class clazz) { - mDManager.getDelegate(DelegateCommon.class).clean(mDb, clazz); + SqlUtil.clean(mDb, clazz); } /** @@ -193,7 +192,7 @@ public class DelegateWrapper { * 查找某张表是否存在 */ public boolean tableExists(Class clazz) { - return mDManager.getDelegate(DelegateCommon.class).tableExists(mDb, clazz); + return SqlUtil.tableExists(mDb, clazz); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 546a84c0..6cfd18cd 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -41,13 +41,10 @@ final class SqlHelper extends SQLiteOpenHelper { private static volatile SqlHelper INSTANCE = null; private Context mContext; - private DelegateCommon mDelegate; - synchronized static SqlHelper init(Context context) { if (INSTANCE == null) { synchronized (SqlHelper.class) { - DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); - INSTANCE = new SqlHelper(context.getApplicationContext(), delegate); + INSTANCE = new SqlHelper(context.getApplicationContext()); } } return INSTANCE; @@ -57,11 +54,10 @@ final class SqlHelper extends SQLiteOpenHelper { return INSTANCE; } - private SqlHelper(Context context, DelegateCommon delegate) { + private SqlHelper(Context context) { super(DBConfig.SAVE_IN_SDCARD ? new DatabaseContext(context) : context, DBConfig.DB_NAME, null, DBConfig.VERSION); mContext = context; - mDelegate = delegate; } @Override public void onOpen(SQLiteDatabase db) { @@ -82,12 +78,11 @@ final class SqlHelper extends SQLiteOpenHelper { } @Override public void onCreate(SQLiteDatabase db) { - DelegateCommon delegate = DelegateManager.getInstance().getDelegate(DelegateCommon.class); Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class clazz = DBConfig.mapping.get(tableName); - if (!delegate.tableExists(db, clazz)) { - delegate.createTable(db, clazz); + if (!SqlUtil.tableExists(db, clazz)) { + SqlUtil.createTable(db, clazz); } } } @@ -167,13 +162,13 @@ final class SqlHelper extends SQLiteOpenHelper { Set tables = DBConfig.mapping.keySet(); for (String tableName : tables) { Class clazz = DBConfig.mapping.get(tableName); - if (mDelegate.tableExists(db, clazz)) { + if (SqlUtil.tableExists(db, clazz)) { //修改表名为中介表名 String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); db.execSQL(alertSql); //创建新表 - mDelegate.createTable(db, clazz); + SqlUtil.createTable(db, clazz); String sql = String.format("SELECT COUNT(*) FROM %s_temp", tableName); Cursor cursor = db.rawQuery(sql, null); @@ -187,7 +182,7 @@ final class SqlHelper extends SQLiteOpenHelper { db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); // 获取新表的所有字段名称 - List newTabColumns = mDelegate.getColumns(clazz); + List newTabColumns = SqlUtil.getColumns(clazz); // 获取旧表的所有字段名称 List oldTabColumns = new ArrayList<>(); @@ -237,9 +232,9 @@ final class SqlHelper extends SQLiteOpenHelper { db.execSQL(insertSql); } //删除中介表 - mDelegate.dropTable(db, tableName + "_temp"); + SqlUtil.dropTable(db, tableName + "_temp"); } else { - mDelegate.createTable(db, clazz); + SqlUtil.createTable(db, clazz); } } db.setTransactionSuccessful(); @@ -281,7 +276,7 @@ final class SqlHelper extends SQLiteOpenHelper { if (url.startsWith("ftp") || url.startsWith("sftp")) { type = ITaskWrapper.D_FTP; } else { - if (mDelegate.tableExists(db, M3U8Entity.class)) { + if (SqlUtil.tableExists(db, M3U8Entity.class)) { Cursor m3u8c = db.rawQuery("SELECT isLive FROM M3U8Entity WHERE filePath=\"" + SqlUtil.encodeStr(filePath) + "\"", null); @@ -386,8 +381,8 @@ final class SqlHelper extends SQLiteOpenHelper { String[] taskTables = new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; for (String taskTable : taskTables) { - if (mDelegate.tableExists(db, taskTable)) { - mDelegate.dropTable(db, taskTable); + if (SqlUtil.tableExists(db, taskTable)) { + SqlUtil.dropTable(db, taskTable); } } @@ -420,8 +415,8 @@ final class SqlHelper extends SQLiteOpenHelper { String[] taskTables = new String[] { "UploadTaskEntity", "DownloadTaskEntity", "DownloadGroupTaskEntity" }; for (String taskTable : taskTables) { - if (mDelegate.tableExists(db, taskTable)) { - mDelegate.dropTable(db, taskTable); + if (SqlUtil.tableExists(db, taskTable)) { + SqlUtil.dropTable(db, taskTable); } } @@ -430,7 +425,7 @@ final class SqlHelper extends SQLiteOpenHelper { String[] keys = new String[] { "downloadPath", "groupName" }; int i = 0; for (String tableName : tables) { - if (!mDelegate.tableExists(db, tableName)) { + if (!SqlUtil.tableExists(db, tableName)) { continue; } String pColumn = keys[i]; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java index c112784f..e3141c6d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -15,6 +15,8 @@ */ package com.arialyy.aria.orm; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.orm.annotation.Default; @@ -26,6 +28,7 @@ import com.arialyy.aria.orm.annotation.One; import com.arialyy.aria.orm.annotation.Primary; import com.arialyy.aria.orm.annotation.Unique; import com.arialyy.aria.orm.annotation.Wrapper; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -41,6 +44,280 @@ import java.util.Set; * sql工具 */ final class SqlUtil { + private static final String TAG = CommonUtil.getClassName("SqlUtil"); + + /** + * 检查表是否存在,不存在则创建表 + */ + static void checkTable(SQLiteDatabase db, Class clazz) { + if (!tableExists(db, clazz)) { + createTable(db, clazz); + } + } + + static void closeCursor(Cursor cursor) { + synchronized (AbsDelegate.class) { + if (cursor != null && !cursor.isClosed()) { + try { + cursor.close(); + } catch (android.database.SQLException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 查找表是否存在 + * + * @param clazz 数据库实体 + * @return true,该数据库实体对应的表存在;false,不存在 + */ + static boolean tableExists(SQLiteDatabase db, Class clazz) { + return tableExists(db, CommonUtil.getClassName(clazz)); + } + + /** + * 查找表是否存在 + * + * @param tableName 表名 + * @return true,该数据库实体对应的表存在;false,不存在 + */ + static boolean tableExists(SQLiteDatabase db, String tableName) { + db = checkDb(db); + Cursor cursor = null; + try { + String sql = + String.format("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", + tableName); + cursor = db.rawQuery(sql, null); + if (cursor != null && cursor.moveToNext()) { + int count = cursor.getInt(0); + if (count > 0) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + closeCursor(cursor); + } + return false; + } + + /** + * 检查list参数是否合法,list只能是{@code List} + * + * @return {@code true} 合法 + */ + static boolean checkList(Field list) { + Class t = CommonUtil.getListParamType(list); + if (t == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持List的参数字段"); + return false; + } + } + + /** + * 检查map参数是否合法,map只能是{@code Map} + * + * @return {@code true} 合法 + */ + static boolean checkMap(Field map) { + Class[] ts = CommonUtil.getMapParamType(map); + if (ts != null + && ts[0] != null + && ts[1] != null + && ts[0] == String.class + && ts[1] == String.class) { + return true; + } else { + ALog.d(TAG, "map参数错误,支持Map的参数字段"); + return false; + } + } + + /** + * 删除指定的表 + */ + static void dropTable(SQLiteDatabase db, String tableName) { + db = checkDb(db); + String deleteSQL = String.format("DROP TABLE IF EXISTS %s", tableName); + //db.beginTransaction(); + db.execSQL(deleteSQL); + //db.setTransactionSuccessful(); + //db.endTransaction(); + } + + /** + * 清空表数据 + */ + static void clean(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + String tableName = CommonUtil.getClassName(clazz); + if (tableExists(db, clazz)) { + String sql = "DELETE FROM " + tableName; + db.execSQL(sql); + } + } + + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + static boolean checkDataExist(SQLiteDatabase db, Class clazz, + String... expression) { + db = checkDb(db); + if (!CommonUtil.checkSqlExpression(expression)) { + return false; + } + String sql = String.format("SELECT rowid, * FROM %s WHERE %s ", CommonUtil.getClassName(clazz), + expression[0]); + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = String.format("'%s'", SqlUtil.encodeStr(expression[i + 1])); + } + sql = String.format(sql, params); + Cursor cursor = db.rawQuery(sql, null); + final boolean isExist = cursor.getCount() > 0; + closeCursor(cursor); + return isExist; + } + + /** + * 通过class 获取该class的表字段 + * + * @return 表字段列表 + */ + static List getColumns(Class clazz) { + List columns = new ArrayList<>(); + List fields = CommonUtil.getAllFields(clazz); + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + columns.add(field.getName()); + } + return columns; + } + + /** + * 检查数据库是否关闭,已经关闭的话,打开数据库 + * + * @return 返回数据库 + */ + static SQLiteDatabase checkDb(SQLiteDatabase db) { + if (db == null || !db.isOpen()) { + db = SqlHelper.getInstance().getDb(); + } + return db; + } + + /** + * 创建表 + * + * @param clazz 数据库实体 + */ + static void createTable(SQLiteDatabase db, Class clazz) { + db = checkDb(db); + List fields = CommonUtil.getAllFields(clazz); + if (fields != null && fields.size() > 0) { + //外键Map,在Sqlite3中foreign修饰的字段必须放在最后 + final List foreignArray = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE IF NOT EXISTS ") + .append(CommonUtil.getClassName(clazz)) + .append(" ("); + for (Field field : fields) { + field.setAccessible(true); + if (SqlUtil.isIgnore(field)) { + continue; + } + Class type = field.getType(); + sb.append(field.getName()); + if (type == String.class || type.isEnum()) { + sb.append(" VARCHAR"); + } else if (type == int.class || type == Integer.class) { + sb.append(" INTEGER"); + } else if (type == float.class || type == Float.class) { + sb.append(" FLOAT"); + } else if (type == double.class || type == Double.class) { + sb.append(" DOUBLE"); + } else if (type == long.class || type == Long.class) { + sb.append(" BIGINT"); + } else if (type == boolean.class || type == Boolean.class) { + sb.append(" BOOLEAN"); + } else if (type == java.util.Date.class || type == java.sql.Date.class) { + sb.append(" DATA"); + } else if (type == byte.class || type == Byte.class) { + sb.append(" BLOB"); + } else if (type == Map.class || type == List.class) { + sb.append(" TEXT"); + } else { + continue; + } + if (SqlUtil.isPrimary(field)) { + Primary pk = field.getAnnotation(Primary.class); + sb.append(" PRIMARY KEY"); + if (pk.autoincrement() && (type == int.class || type == Integer.class)) { + sb.append(" AUTOINCREMENT"); + } + } + + if (SqlUtil.isForeign(field)) { + foreignArray.add(field); + } + + if (SqlUtil.isNoNull(field)) { + sb.append(" NOT NULL"); + } + + if (SqlUtil.isDefault(field)) { + Default d = field.getAnnotation(Default.class); + if (!TextUtils.isEmpty(d.value())) { + sb.append(" ERROR ").append("'").append(d.value()).append("'"); + } + } + + if (SqlUtil.isUnique(field)) { + sb.append(" UNIQUE"); + } + + sb.append(","); + } + + for (Field field : foreignArray) { + Foreign foreign = field.getAnnotation(Foreign.class); + sb.append("FOREIGN KEY (") + .append(field.getName()) + .append(") REFERENCES ") + .append(CommonUtil.getClassName(foreign.parent())) + .append("(") + .append(foreign.column()) + .append(")"); + ActionPolicy update = foreign.onUpdate(); + ActionPolicy delete = foreign.onDelete(); + if (update != ActionPolicy.NO_ACTION) { + sb.append(" ON UPDATE ").append(update.function); + } + + if (delete != ActionPolicy.NO_ACTION) { + sb.append(" ON DELETE ").append(update.function); + } + sb.append(","); + } + + String str = sb.toString(); + str = str.substring(0, str.length() - 1) + ");"; + db.execSQL(str); + } + } /** * URL编码字符串 @@ -56,7 +333,7 @@ final class SqlUtil { /** * 获取主键字段名 */ - static String getPrimaryName(Class clazz) { + static String getPrimaryName(Class clazz) { List fields = CommonUtil.getAllFields(clazz); String column; if (fields != null && !fields.isEmpty()) { @@ -201,8 +478,8 @@ final class SqlUtil { * * @return {@code true} 是 */ - static boolean isWrapper(Class clazz) { - Wrapper w = (Wrapper) clazz.getAnnotation(Wrapper.class); + static boolean isWrapper(Class clazz) { + Wrapper w = clazz.getAnnotation(Wrapper.class); return w != null; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 5ea85006..75d9a2a0 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -28,6 +28,7 @@ import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Proxy; import java.util.List; /** diff --git a/README.md b/README.md index 16f18028..4d93ca41 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,16 @@ Aria有以下特点: ## 引入库 [![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) -[![Core](https://img.shields.io/badge/Core-3.8-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.8-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8-orange)](https://github.com/AriaLyy/Aria) +[![Core](https://img.shields.io/badge/Core-3.8.1-blue)](https://github.com/AriaLyy/Aria) +[![Compiler](https://img.shields.io/badge/Compiler-3.8.1-blue)](https://github.com/AriaLyy/Aria) +[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.8.1-orange)](https://github.com/AriaLyy/Aria) +[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.8.1-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.8' -annotationProcessor 'com.arialyy.aria:compiler:3.8' -implementation 'com.arialyy.aria:ftpComponent:3.8' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.8' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.8.1' +annotationProcessor 'com.arialyy.aria:compiler:3.8.1' +implementation 'com.arialyy.aria:ftpComponent:3.8.1' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.8.1' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,19 +137,12 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.8 (2019/12/17) - - 移除androidx和support的依赖,现在无论是哪个版本的appcompat包都可以使用本框架 - - 修复一个在xml中使用fragment导致的内存泄漏问题 - - m3u8协议的key信息增加了`keyFormat`,`keyFormatVersion`字段 - - m3u8增加了`ignoreFailureTs`方法,忽略下载失败的ts切片 - - 修复在dialogFragment的`onCreateDialog()`注册导致的注解不生效问题 - - 修复组合任务初始化失败时,无法删除的问题 - - 修复`reStart()`后,无法停止的问题 - - ftp增加主动模式,开启主动模式:https://aria.laoyuyu.me/aria_doc/api/ftp_params.html - - 修复ftp服务器无法响应`abor`命令导致的无法停止上传的问题 https://github.com/AriaLyy/Aria/issues/564 - - 修复ftp上传时,服务器有长度为0的文件导致上传失败的问题 - - 修复下载任务和上传任务的文件路径是同一个时,导致的记录混乱问题 - - 优化提示 ++ v_3.8.1 (2019/12/22) + - 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570 + - 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 + - 修复一个服务器端无法创建socket连接,却没有返回码导致客户端卡住的问题 https://github.com/AriaLyy/Aria/issues/569 + - 修复文件删除后,组合任务没有重新下载的问题 https://github.com/AriaLyy/Aria/issues/574 + - 优化缓存队列和执行队列 [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index cb8d1c24..e4ea2d42 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -32,7 +32,7 @@ 3、只对新的多线程下载任务有效 4、只对多线程的任务有效 --> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index 16881fc4..cbf32eb2 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -117,6 +117,8 @@ public class MainActivity extends BaseActivity { module.startNextActivity(MainActivity.this, data.get(position), M3U8LiveDLoadActivity.class); break; + case 8: // sftp + break; } } }); diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 998f1ed5..2b9980a1 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -97,7 +97,7 @@ public class DownloadGroupActivity extends BaseActivity + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62666bbf..8a0c0cc9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,14 +32,15 @@ 文件名为空 - http 下载 - http 上传 - http 组合任务下载 + HTTP 下载 + HTTP 上传 + HTTP 组合任务下载 FTP 下载 FTP 文件夹下载 FTP 文件上传 M3U8 点播文件下载 M3U8 直播文件下载 + SFTP 下载 @@ -51,6 +52,7 @@ 上传单个文件到服务器 下载M3U8点播文件 下载M3U8直播文件 + SFTP单文件下载 diff --git a/build.gradle b/build.gradle index c5910ecb..63797bf6 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 380 - versionName = '3.8' + versionCode = 381 + versionName = '3.8.1' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 0cb5d3e68461842509c569f76610654b8d5e6298 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sun, 22 Dec 2019 11:39:37 +0800 Subject: [PATCH 039/140] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- app/src/main/assets/aria_config.xml | 2 +- img/ic_launcher.png | Bin 0 -> 7323 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 img/ic_launcher.png diff --git a/README.md b/README.md index 4d93ca41..bed787dc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Aria -![图标](https://github.com/AriaLyy/DownloadUtil/blob/v_3.0/app/src/main/res/mipmap-hdpi/ic_launcher.png)
+![图标](https://github.com/AriaLyy/DownloadUtil/blob/master/img/ic_launcher.png)
## [ENGLISH DOC](https://github.com/AriaLyy/Aria/blob/master/ENGLISH_README.md)
## [中文文档](https://aria.laoyuyu.me/aria_doc) Aria项目源于工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。 diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml index e4ea2d42..cb8d1c24 100644 --- a/app/src/main/assets/aria_config.xml +++ b/app/src/main/assets/aria_config.xml @@ -32,7 +32,7 @@ 3、只对新的多线程下载任务有效 4、只对多线程的任务有效 --> - + - + diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java index cbf32eb2..8ac51204 100644 --- a/app/src/main/java/com/arialyy/simple/MainActivity.java +++ b/app/src/main/java/com/arialyy/simple/MainActivity.java @@ -29,7 +29,6 @@ import androidx.lifecycle.ViewModelProviders; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.arialyy.aria.core.AriaManager; import com.arialyy.frame.permission.OnPermissionCallback; import com.arialyy.frame.permission.PermissionManager; import com.arialyy.frame.util.show.T; diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index c6b238f6..801b91f0 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.StatFs; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -32,7 +33,6 @@ import androidx.lifecycle.ViewModelProviders; import com.arialyy.annotations.Download; import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.common.HttpOption; -import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.listener.ISchedulers; @@ -40,16 +40,13 @@ import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; -import com.arialyy.aria.util.FileUtil; import com.arialyy.frame.util.show.T; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.common.ModifyPathDialog; import com.arialyy.simple.common.ModifyUrlDialog; import com.arialyy.simple.databinding.ActivitySingleBinding; -import com.arialyy.simple.util.AppUtil; import java.io.File; -import java.io.IOException; import java.util.List; import java.util.Map; diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java b/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java index d35708f4..907e39ce 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/GroupModule.java @@ -54,6 +54,13 @@ public class GroupModule extends BaseModule { return names; } + List getSubName1() { + List names = new ArrayList<>(); + String[] str = getContext().getResources().getStringArray(R.array.group_names); + Collections.addAll(names, str); + return names; + } + List getSubName2() { List taskSubFile; taskSubFile = new ArrayList<>(); diff --git a/build.gradle b/build.gradle index 63797bf6..84852cd5 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 381 - versionName = '3.8.1' + versionCode = 383 + versionName = '3.8.3_beta' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From bfc2cff5b0ab4e860cee1e3232fcdc50ca009f2d Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sat, 28 Dec 2019 13:14:49 +0800 Subject: [PATCH 044/140] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ftp=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=A0=87=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../arialyy/aria/core/common/FtpOption.java | 274 ++ .../aria/core/download/DownloadReceiver.java | 2 +- .../aria/core/upload/UploadReceiver.java | 2 +- AriaFtpPlug/.gitignore | 1 - AriaFtpPlug/bintray-release.gradle | 11 - AriaFtpPlug/build.gradle | 14 - AriaFtpPlug/proguard-rules.pro | 25 - .../commons/net/DatagramSocketClient.java | 287 -- .../commons/net/DatagramSocketFactory.java | 67 - .../net/DefaultDatagramSocketFactory.java | 72 - .../commons/net/DefaultSocketFactory.java | 204 - .../net/MalformedServerReplyException.java | 52 - .../commons/net/PrintCommandListener.java | 190 - .../commons/net/ProtocolCommandEvent.java | 139 - .../commons/net/ProtocolCommandListener.java | 58 - .../commons/net/ProtocolCommandSupport.java | 123 - .../aria/apache/commons/net/SocketClient.java | 869 ---- .../apache/commons/net/ftp/Configurable.java | 34 - .../java/aria/apache/commons/net/ftp/FTP.java | 1777 -------- .../apache/commons/net/ftp/FTPClient.java | 3787 ----------------- .../commons/net/ftp/FTPClientConfig.java | 713 ---- .../aria/apache/commons/net/ftp/FTPCmd.java | 75 - .../apache/commons/net/ftp/FTPCommand.java | 170 - .../net/ftp/FTPConnectionClosedException.java | 52 - .../aria/apache/commons/net/ftp/FTPFile.java | 493 --- .../commons/net/ftp/FTPFileEntryParser.java | 129 - .../net/ftp/FTPFileEntryParserImpl.java | 72 - .../apache/commons/net/ftp/FTPFileFilter.java | 34 - .../commons/net/ftp/FTPFileFilters.java | 54 - .../apache/commons/net/ftp/FTPHTTPClient.java | 194 - .../commons/net/ftp/FTPListParseEngine.java | 311 -- .../aria/apache/commons/net/ftp/FTPReply.java | 193 - .../apache/commons/net/ftp/FTPSClient.java | 931 ---- .../apache/commons/net/ftp/FTPSCommand.java | 52 - .../net/ftp/FTPSServerSocketFactory.java | 72 - .../commons/net/ftp/FTPSSocketFactory.java | 116 - .../commons/net/ftp/FTPSTrustManager.java | 54 - .../net/ftp/OnFtpInputStreamListener.java | 33 - .../apache/commons/net/ftp/package-info.java | 21 - .../ftp/parser/CompositeFileEntryParser.java | 59 - .../ConfigurableFTPFileEntryParserImpl.java | 123 - .../DefaultFTPFileEntryParserFactory.java | 254 -- .../parser/EnterpriseUnixFTPEntryParser.java | 159 - .../ftp/parser/FTPFileEntryParserFactory.java | 63 - .../net/ftp/parser/FTPTimestampParser.java | 53 - .../ftp/parser/FTPTimestampParserImpl.java | 403 -- .../net/ftp/parser/MLSxEntryParser.java | 260 -- .../net/ftp/parser/MVSFTPEntryParser.java | 537 --- .../ftp/parser/MacOsPeterFTPEntryParser.java | 245 -- .../net/ftp/parser/NTFTPEntryParser.java | 142 - .../net/ftp/parser/NetwareFTPEntryParser.java | 175 - .../net/ftp/parser/OS2FTPEntryParser.java | 127 - .../net/ftp/parser/OS400FTPEntryParser.java | 399 -- .../parser/ParserInitializationException.java | 60 - .../parser/RegexFTPFileEntryParserImpl.java | 199 - .../net/ftp/parser/UnixFTPEntryParser.java | 335 -- .../net/ftp/parser/VMSFTPEntryParser.java | 251 -- .../parser/VMSVersioningFTPEntryParser.java | 153 - .../commons/net/ftp/parser/package-info.java | 21 - .../apache/commons/net/io/CRLFLineReader.java | 75 - .../commons/net/io/CopyStreamAdapter.java | 111 - .../commons/net/io/CopyStreamEvent.java | 113 - .../commons/net/io/CopyStreamException.java | 69 - .../commons/net/io/CopyStreamListener.java | 68 - .../net/io/DotTerminatedMessageReader.java | 238 -- .../net/io/DotTerminatedMessageWriter.java | 189 - .../net/io/FromNetASCIIInputStream.java | 196 - .../net/io/FromNetASCIIOutputStream.java | 150 - .../commons/net/io/SocketInputStream.java | 64 - .../commons/net/io/SocketOutputStream.java | 80 - .../commons/net/io/ToNetASCIIInputStream.java | 165 - .../net/io/ToNetASCIIOutputStream.java | 105 - .../java/aria/apache/commons/net/io/Util.java | 351 -- .../apache/commons/net/io/package-info.java | 21 - .../aria/apache/commons/net/util/Base64.java | 1056 ----- .../apache/commons/net/util/Charsets.java | 53 - .../commons/net/util/KeyManagerUtils.java | 236 - .../apache/commons/net/util/ListenerList.java | 59 - .../commons/net/util/SSLContextUtils.java | 77 - .../commons/net/util/SSLSocketUtils.java | 68 - .../apache/commons/net/util/SubnetUtils.java | 395 -- .../commons/net/util/TrustManagerUtils.java | 113 - .../apache/commons/net/util/package-info.java | 21 - DEV_LOG.md | 1 + .../arialyy/aria/ftp/AbsFtpInfoThread.java | 2 +- .../com/arialyy/aria/ftp/FtpTaskOption.java | 27 + .../arialyy/aria/http/HttpRecordAdapter.java | 2 +- .../aria/core/common/RecordHandler.java | 15 - .../aria/core/common/RecordHelper.java | 31 +- .../aria/core/loader/ThreadStateManager.java | 5 + .../java/com/arialyy/aria/orm/DBConfig.java | 2 +- .../java/com/arialyy/aria/orm/SqlHelper.java | 79 +- .../java/com/arialyy/aria/orm/SqlUtil.java | 66 +- .../core/download/FtpDownloadActivity.java | 5 +- .../core/download/FtpDownloadModule.java | 3 +- .../core/download/SingleTaskActivity.java | 4 +- build.gradle | 2 +- settings.gradle | 2 +- 98 files changed, 444 insertions(+), 19650 deletions(-) delete mode 100644 AriaFtpPlug/.gitignore delete mode 100644 AriaFtpPlug/bintray-release.gradle delete mode 100644 AriaFtpPlug/build.gradle delete mode 100644 AriaFtpPlug/proguard-rules.pro delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketClient.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/PrintCommandListener.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/SocketClient.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/Configurable.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTP.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClient.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFile.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPReply.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/package-info.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamException.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketInputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/Util.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/io/package-info.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Base64.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Charsets.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/ListenerList.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SubnetUtils.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java delete mode 100644 AriaFtpPlug/src/main/java/aria/apache/commons/net/util/package-info.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java index c162cd83..bc4fcf69 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/FtpOption.java @@ -21,6 +21,12 @@ import com.arialyy.aria.core.ProtocolType; import com.arialyy.aria.core.processor.IFtpUploadInterceptor; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; +import java.text.DateFormatSymbols; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeMap; /** * Created by laoyuyu on 2018/3/9. @@ -37,6 +43,15 @@ public class FtpOption extends BaseOption { private int minPort, maxPort; private String activeExternalIPAddress; + //---------------- ftp client 配置信息 start + private String defaultDateFormatStr = null; + private String recentDateFormatStr = null; + private String serverLanguageCode = null; + private String shortMonthNames = null; + private String serverTimeZoneId = null; + private String systemKey = FTPServerIdentifier.SYST_UNIX; + //---------------- ftp client 配置信息 end + public FtpOption() { super(); } @@ -203,6 +218,78 @@ public class FtpOption extends BaseOption { return this; } + /** + * 设置ftp服务器所在的操作系统的标志,如果出现文件获取失败,请设置该标志为 + * 默认使用{@link FTPServerIdentifier#SYST_UNIX} + * + * @param identifier {@link FTPServerIdentifier} + */ + public FtpOption setServerIdentifier(String identifier) { + this.systemKey = identifier; + return this; + } + + /** + * 解析ftp信息时,默认的文件日期格式,如:setDefaultDateFormatStr("d MMM yyyy") + * + * @param defaultDateFormatStr 日期格式 + */ + public FtpOption setDefaultDateFormatStr(String defaultDateFormatStr) { + this.defaultDateFormatStr = defaultDateFormatStr; + return this; + } + + /** + * 解析ftp信息时,默认的文件修改日期格式,如:setRecentDateFormatStr("d MMM HH:mm") + * + * @param recentDateFormatStr 日期格式 + */ + public FtpOption setRecentDateFormatStr(String recentDateFormatStr) { + this.recentDateFormatStr = recentDateFormatStr; + return this; + } + + /** + * 设置服务器使用的时区,java.util.TimeZone,如:America/Chicago or Asia/Rangoon. + * + * @param serverTimeZoneId 时区 + */ + public void setServerTimeZoneId(String serverTimeZoneId) { + this.serverTimeZoneId = serverTimeZoneId; + } + + /** + *

+ * setter for the shortMonthNames property. + * This property allows the user to specify a set of month names + * used by the server that is different from those that may be + * specified using the {@link #setServerLanguageCode(String) serverLanguageCode} + * property. + *

+ * This should be a string containing twelve strings each composed of + * three characters, delimited by pipe (|) characters. Currently, + * only 8-bit ASCII characters are known to be supported. For example, + * a set of month names used by a hypothetical Icelandic FTP server might + * conceivably be specified as + * "jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des". + *

+ * + * @param shortMonthNames The value to set to the shortMonthNames property. + */ + public void setShortMonthNames(String shortMonthNames) { + this.shortMonthNames = shortMonthNames; + } + + /** + * 设置服务器语言代码 + * + * @param serverLanguageCode {@link #LANGUAGE_CODE_MAP} + */ + public FtpOption setServerLanguageCode(String serverLanguageCode) { + this.serverLanguageCode = serverLanguageCode; + return this; + } + public void setUrlEntity(FtpUrlEntity urlEntity) { this.urlEntity = urlEntity; urlEntity.needLogin = isNeedLogin; @@ -218,4 +305,191 @@ public class FtpOption extends BaseOption { urlEntity.isImplicit = isImplicit; } } + + /** + * ftp 服务器所在的操作系统标志 + */ + public interface FTPServerIdentifier { + /** + * Identifier by which a unix-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_UNIX = "UNIX"; + + /** + * Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are + * trimmed from file names. This is to maintain backwards compatibility with + * the original behaviour of the parser which ignored multiple spaces between the date + * and the start of the file name. + * + * @since 3.4 + */ + String SYST_UNIX_TRIM_LEADING = "UNIX_LTRIM"; + + /** + * Identifier by which a vms-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_VMS = "VMS"; + + /** + * Identifier by which a WindowsNT-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_NT = "WINDOWS"; + + /** + * Identifier by which an OS/2-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_OS2 = "OS/2"; + + /** + * Identifier by which an OS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_OS400 = "OS/400"; + + /** + * Identifier by which an AS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_AS400 = "AS/400"; + + /** + * Identifier by which an MVS-based ftp server is known throughout + * the commons-net ftp system. + */ + String SYST_MVS = "MVS"; + + /** + * Some servers return an "UNKNOWN Type: L8" message + * in response to the SYST command. We set these to be a Unix-type system. + * This may happen if the ftpd in question was compiled without system + * information. + * + * NET-230 - Updated to be UPPERCASE so that the check done in + * createFileEntryParser will succeed. + * + * @since 1.5 + */ + String SYST_L8 = "TYPE: L8"; + + /** + * Identifier by which an Netware-based ftp server is known throughout + * the commons-net ftp system. + * + * @since 1.5 + */ + String SYST_NETWARE = "NETWARE"; + + /** + * Identifier by which a Mac pre OS-X -based ftp server is known throughout + * the commons-net ftp system. + * + * @since 3.1 + */ + // Full string is "MACOS Peter's Server"; the substring below should be enough + String SYST_MACOS_PETER = "MACOS PETER"; // NET-436 + } + + /** + * 支持的语言代码 + */ + private static final Map LANGUAGE_CODE_MAP = new TreeMap<>(); + + static { + + // if there are other commonly used month name encodings which + // correspond to particular locales, please add them here. + + // many locales code short names for months as all three letters + // these we handle simply. + LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); + LANGUAGE_CODE_MAP.put("de", Locale.GERMAN); + LANGUAGE_CODE_MAP.put("it", Locale.ITALIAN); + LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish + LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese + LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish + LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish + LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian + LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch + LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian + LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian + LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian + LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak + LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian + + // some don't + LANGUAGE_CODE_MAP.put("fr", + "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french + } + + /** + * Looks up the supplied language code in the internally maintained table of + * language codes. Returns a DateFormatSymbols object configured with + * short month names corresponding to the code. If there is no corresponding + * entry in the table, the object returned will be that for + * Locale.US + * + * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} + * @return a DateFormatSymbols object configured with short month names + * corresponding to the supplied code, or with month names for + * Locale.US if there is no corresponding entry in the internal + * table. + */ + public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) { + Object lang = LANGUAGE_CODE_MAP.get(languageCode); + if (lang != null) { + if (lang instanceof Locale) { + return new DateFormatSymbols((Locale) lang); + } else if (lang instanceof String) { + return getDateFormatSymbols((String) lang); + } + } + return new DateFormatSymbols(Locale.US); + } + + /** + * Returns a DateFormatSymbols object configured with short month names + * as in the supplied string + * + * @param shortmonths This should be as described in + * {@link #setShortMonthNames(String) shortMonthNames} + * @return a DateFormatSymbols object configured with short month names + * as in the supplied string + */ + public static DateFormatSymbols getDateFormatSymbols(String shortmonths) { + String[] months = splitShortMonthString(shortmonths); + DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); + dfs.setShortMonths(months); + return dfs; + } + + private static String[] splitShortMonthString(String shortmonths) { + StringTokenizer st = new StringTokenizer(shortmonths, "|"); + int monthcnt = st.countTokens(); + if (12 != monthcnt) { + throw new IllegalArgumentException("expecting a pipe-delimited string containing 12 tokens"); + } + String[] months = new String[13]; + int pos = 0; + while (st.hasMoreTokens()) { + months[pos++] = st.nextToken(); + } + months[pos] = ""; + return months; + } + + /** + * Returns a Collection of all the language codes currently supported + * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} + * for a functional descrption of language codes within this system. + * + * @return a Collection of all the language codes currently supported + * by this class + */ + public static Collection getSupportedLanguageCodes() { + return LANGUAGE_CODE_MAP.keySet(); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 09c58c08..5382cd4f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -181,7 +181,7 @@ public class DownloadReceiver extends AbsReceiver { } } } else { - ALog.e(TAG, "没有Aria的注解方法"); + ALog.e(TAG, "没有Aria的注解方法,详情见:https://aria.laoyuyu.me/aria_doc/other/annotaion_invalid.html"); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index c8c8ec2e..7b5aea7b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -267,7 +267,7 @@ public class UploadReceiver extends AbsReceiver { } } } else { - ALog.e(TAG, "没有Aria的注解方法"); + ALog.e(TAG, "没有Aria的注解方法,详情见:https://aria.laoyuyu.me/aria_doc/other/annotaion_invalid.html"); } } diff --git a/AriaFtpPlug/.gitignore b/AriaFtpPlug/.gitignore deleted file mode 100644 index 796b96d1..00000000 --- a/AriaFtpPlug/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/AriaFtpPlug/bintray-release.gradle b/AriaFtpPlug/bintray-release.gradle deleted file mode 100644 index 43708895..00000000 --- a/AriaFtpPlug/bintray-release.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'com.novoda.bintray-release' -publish { - artifactId = 'aria-ftp-plug' - userOrg = rootProject.ext.userOrg - groupId = rootProject.ext.groupId - uploadName = 'FtpPlug' - publishVersion = rootProject.ext.publishVersion - desc = rootProject.ext.desc - website = rootProject.ext.website - licences = rootProject.ext.licences -} \ No newline at end of file diff --git a/AriaFtpPlug/build.gradle b/AriaFtpPlug/build.gradle deleted file mode 100644 index 71876c9d..00000000 --- a/AriaFtpPlug/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'java' - -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" -} - -sourceCompatibility = JavaVersion.VERSION_1_7 -targetCompatibility = JavaVersion.VERSION_1_7 - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) -} - -//apply from: 'bintray-release.gradle' \ No newline at end of file diff --git a/AriaFtpPlug/proguard-rules.pro b/AriaFtpPlug/proguard-rules.pro deleted file mode 100644 index 36df18ca..00000000 --- a/AriaFtpPlug/proguard-rules.pro +++ /dev/null @@ -1,25 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in E:\sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketClient.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketClient.java deleted file mode 100644 index 2ddf9be4..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketClient.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; -import java.nio.charset.Charset; - -/*** - * The DatagramSocketClient provides the basic operations that are required - * of client objects accessing datagram sockets. It is meant to be - * subclassed to avoid having to rewrite the same code over and over again - * to open a socket, close a socket, set timeouts, etc. Of special note - * is the {@link #setDatagramSocketFactory setDatagramSocketFactory } - * method, which allows you to control the type of DatagramSocket the - * DatagramSocketClient creates for network communications. This is - * especially useful for adding things like proxy support as well as better - * support for applets. For - * example, you could create a - * {@link DatagramSocketFactory} - * that - * requests browser security capabilities before creating a socket. - * All classes derived from DatagramSocketClient should use the - * {@link #_socketFactory_ _socketFactory_ } member variable to - * create DatagramSocket instances rather than instantiating - * them by directly invoking a constructor. By honoring this contract - * you guarantee that a user will always be able to provide his own - * Socket implementations by substituting his own SocketFactory. - * - * - * @see DatagramSocketFactory - ***/ - -public abstract class DatagramSocketClient { - /*** - * The default DatagramSocketFactory shared by all DatagramSocketClient - * instances. - ***/ - private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY = - new DefaultDatagramSocketFactory(); - - /** - * Charset to use for byte IO. - */ - private Charset charset = Charset.defaultCharset(); - - /*** The timeout to use after opening a socket. ***/ - protected int _timeout_; - - /*** The datagram socket used for the connection. ***/ - protected DatagramSocket _socket_; - - /*** - * A status variable indicating if the client's socket is currently open. - ***/ - protected boolean _isOpen_; - - /*** The datagram socket's DatagramSocketFactory. ***/ - protected DatagramSocketFactory _socketFactory_; - - /*** - * Default constructor for DatagramSocketClient. Initializes - * _socket_ to null, _timeout_ to 0, and _isOpen_ to false. - ***/ - public DatagramSocketClient() { - _socket_ = null; - _timeout_ = 0; - _isOpen_ = false; - _socketFactory_ = __DEFAULT_SOCKET_FACTORY; - } - - /*** - * Opens a DatagramSocket on the local host at the first available port. - * Also sets the timeout on the socket to the default timeout set - * by {@link #setDefaultTimeout setDefaultTimeout() }. - *

- * _isOpen_ is set to true after calling this method and _socket_ - * is set to the newly opened socket. - * - * @throws SocketException If the socket could not be opened or the - * timeout could not be set. - ***/ - public void open() throws SocketException { - _socket_ = _socketFactory_.createDatagramSocket(); - _socket_.setSoTimeout(_timeout_); - _isOpen_ = true; - } - - /*** - * Opens a DatagramSocket on the local host at a specified port. - * Also sets the timeout on the socket to the default timeout set - * by {@link #setDefaultTimeout setDefaultTimeout() }. - *

- * _isOpen_ is set to true after calling this method and _socket_ - * is set to the newly opened socket. - * - * @param port The port to use for the socket. - * @throws SocketException If the socket could not be opened or the - * timeout could not be set. - ***/ - public void open(int port) throws SocketException { - _socket_ = _socketFactory_.createDatagramSocket(port); - _socket_.setSoTimeout(_timeout_); - _isOpen_ = true; - } - - /*** - * Opens a DatagramSocket at the specified address on the local host - * at a specified port. - * Also sets the timeout on the socket to the default timeout set - * by {@link #setDefaultTimeout setDefaultTimeout() }. - *

- * _isOpen_ is set to true after calling this method and _socket_ - * is set to the newly opened socket. - * - * @param port The port to use for the socket. - * @param laddr The local address to use. - * @throws SocketException If the socket could not be opened or the - * timeout could not be set. - ***/ - public void open(int port, InetAddress laddr) throws SocketException { - _socket_ = _socketFactory_.createDatagramSocket(port, laddr); - _socket_.setSoTimeout(_timeout_); - _isOpen_ = true; - } - - /*** - * Closes the DatagramSocket used for the connection. - * You should call this method after you've finished using the class - * instance and also before you call {@link #open open() } - * again. _isOpen_ is set to false and _socket_ is set to null. - ***/ - public void close() { - if (_socket_ != null) { - _socket_.close(); - } - _socket_ = null; - _isOpen_ = false; - } - - /*** - * Returns true if the client has a currently open socket. - * - * @return True if the client has a currently open socket, false otherwise. - ***/ - public boolean isOpen() { - return _isOpen_; - } - - /*** - * Set the default timeout in milliseconds to use when opening a socket. - * After a call to open, the timeout for the socket is set using this value. - * This method should be used prior to a call to {@link #open open()} - * and should not be confused with {@link #setSoTimeout setSoTimeout()} - * which operates on the currently open socket. _timeout_ contains - * the new timeout value. - * - * @param timeout The timeout in milliseconds to use for the datagram socket - * connection. - ***/ - public void setDefaultTimeout(int timeout) { - _timeout_ = timeout; - } - - /*** - * Returns the default timeout in milliseconds that is used when - * opening a socket. - * - * @return The default timeout in milliseconds that is used when - * opening a socket. - ***/ - public int getDefaultTimeout() { - return _timeout_; - } - - /*** - * Set the timeout in milliseconds of a currently open connection. - * Only call this method after a connection has been opened - * by {@link #open open()}. - * - * @param timeout The timeout in milliseconds to use for the currently - * open datagram socket connection. - * @throws SocketException if an error setting the timeout - ***/ - public void setSoTimeout(int timeout) throws SocketException { - _socket_.setSoTimeout(timeout); - } - - /*** - * Returns the timeout in milliseconds of the currently opened socket. - * If you call this method when the client socket is not open, - * a NullPointerException is thrown. - * - * @return The timeout in milliseconds of the currently opened socket. - * @throws SocketException if an error getting the timeout - ***/ - public int getSoTimeout() throws SocketException { - return _socket_.getSoTimeout(); - } - - /*** - * Returns the port number of the open socket on the local host used - * for the connection. If you call this method when the client socket - * is not open, a NullPointerException is thrown. - * - * @return The port number of the open socket on the local host used - * for the connection. - ***/ - public int getLocalPort() { - return _socket_.getLocalPort(); - } - - /*** - * Returns the local address to which the client's socket is bound. - * If you call this method when the client socket is not open, a - * NullPointerException is thrown. - * - * @return The local address to which the client's socket is bound. - ***/ - public InetAddress getLocalAddress() { - return _socket_.getLocalAddress(); - } - - /*** - * Sets the DatagramSocketFactory used by the DatagramSocketClient - * to open DatagramSockets. If the factory value is null, then a default - * factory is used (only do this to reset the factory after having - * previously altered it). - * - * @param factory The new DatagramSocketFactory the DatagramSocketClient - * should use. - ***/ - public void setDatagramSocketFactory(DatagramSocketFactory factory) { - if (factory == null) { - _socketFactory_ = __DEFAULT_SOCKET_FACTORY; - } else { - _socketFactory_ = factory; - } - } - - /** - * Gets the charset name. - * - * @return the charset name. - * @since 3.3 - * TODO Will be deprecated once the code requires Java 1.6 as a mininmum - */ - public String getCharsetName() { - return charset.name(); - } - - /** - * Gets the charset. - * - * @return the charset. - * @since 3.3 - */ - public Charset getCharset() { - return charset; - } - - /** - * Sets the charset. - * - * @param charset the charset. - * @since 3.3 - */ - public void setCharset(Charset charset) { - this.charset = charset; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java deleted file mode 100644 index 385cceb4..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DatagramSocketFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; - -/*** - * The DatagramSocketFactory interface provides a means for the - * programmer to control the creation of datagram sockets and - * provide his own DatagramSocket implementations for use by all - * classes derived from - * {@link DatagramSocketClient} - * . - * This allows you to provide your own DatagramSocket implementations and - * to perform security checks or browser capability requests before - * creating a DatagramSocket. - * - * - ***/ - -public interface DatagramSocketFactory { - - /*** - * Creates a DatagramSocket on the local host at the first available port. - * @return the socket - * - * @throws SocketException If the socket could not be created. - ***/ - public DatagramSocket createDatagramSocket() throws SocketException; - - /*** - * Creates a DatagramSocket on the local host at a specified port. - * - * @param port The port to use for the socket. - * @return the socket - * @throws SocketException If the socket could not be created. - ***/ - public DatagramSocket createDatagramSocket(int port) throws SocketException; - - /*** - * Creates a DatagramSocket at the specified address on the local host - * at a specified port. - * - * @param port The port to use for the socket. - * @param laddr The local address to use. - * @return the socket - * @throws SocketException If the socket could not be created. - ***/ - public DatagramSocket createDatagramSocket(int port, InetAddress laddr) throws SocketException; -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java deleted file mode 100644 index e269124d..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultDatagramSocketFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; - -/*** - * DefaultDatagramSocketFactory implements the DatagramSocketFactory - * interface by simply wrapping the java.net.DatagramSocket - * constructors. It is the default DatagramSocketFactory used by - * {@link DatagramSocketClient} - * implementations. - * - * - * @see DatagramSocketFactory - * @see DatagramSocketClient - * @see DatagramSocketClient#setDatagramSocketFactory - ***/ - -public class DefaultDatagramSocketFactory implements DatagramSocketFactory { - - /*** - * Creates a DatagramSocket on the local host at the first available port. - * @return a new DatagramSocket - * @throws SocketException If the socket could not be created. - ***/ - @Override public DatagramSocket createDatagramSocket() throws SocketException { - return new DatagramSocket(); - } - - /*** - * Creates a DatagramSocket on the local host at a specified port. - * - * @param port The port to use for the socket. - * @return a new DatagramSocket - * @throws SocketException If the socket could not be created. - ***/ - @Override public DatagramSocket createDatagramSocket(int port) throws SocketException { - return new DatagramSocket(port); - } - - /*** - * Creates a DatagramSocket at the specified address on the local host - * at a specified port. - * - * @param port The port to use for the socket. - * @param laddr The local address to use. - * @return a new DatagramSocket - * @throws SocketException If the socket could not be created. - ***/ - @Override public DatagramSocket createDatagramSocket(int port, InetAddress laddr) - throws SocketException { - return new DatagramSocket(port, laddr); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java deleted file mode 100644 index 1fec9085..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/DefaultSocketFactory.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.SocketFactory; - -/*** - * DefaultSocketFactory implements the SocketFactory interface by - * simply wrapping the java.net.Socket and java.net.ServerSocket - * constructors. It is the default SocketFactory used by - * {@link SocketClient} - * implementations. - * - * - * @see SocketFactory - * @see SocketClient - * @see SocketClient#setSocketFactory - ***/ - -public class DefaultSocketFactory extends SocketFactory { - /** The proxy to use when creating new sockets. */ - private final Proxy connProxy; - - /** - * The default constructor. - */ - public DefaultSocketFactory() { - this(null); - } - - /** - * A constructor for sockets with proxy support. - * - * @param proxy The Proxy to use when creating new Sockets. - * @since 3.2 - */ - public DefaultSocketFactory(Proxy proxy) { - connProxy = proxy; - } - - /** - * Creates an unconnected Socket. - * - * @return A new unconnected Socket. - * @throws IOException If an I/O error occurs while creating the Socket. - * @since 3.2 - */ - @Override public Socket createSocket() throws IOException { - if (connProxy != null) { - return new Socket(connProxy); - } - return new Socket(); - } - - /*** - * Creates a Socket connected to the given host and port. - * - * @param host The hostname to connect to. - * @param port The port to connect to. - * @return A Socket connected to the given host and port. - * @throws UnknownHostException If the hostname cannot be resolved. - * @throws IOException If an I/O error occurs while creating the Socket. - ***/ - @Override public Socket createSocket(String host, int port) - throws UnknownHostException, IOException { - if (connProxy != null) { - Socket s = new Socket(connProxy); - s.connect(new InetSocketAddress(host, port)); - return s; - } - return new Socket(host, port); - } - - /*** - * Creates a Socket connected to the given host and port. - * - * @param address The address of the host to connect to. - * @param port The port to connect to. - * @return A Socket connected to the given host and port. - * @throws IOException If an I/O error occurs while creating the Socket. - ***/ - @Override public Socket createSocket(InetAddress address, int port) throws IOException { - if (connProxy != null) { - Socket s = new Socket(connProxy); - s.connect(new InetSocketAddress(address, port)); - return s; - } - return new Socket(address, port); - } - - /*** - * Creates a Socket connected to the given host and port and - * originating from the specified local address and port. - * - * @param host The hostname to connect to. - * @param port The port to connect to. - * @param localAddr The local address to use. - * @param localPort The local port to use. - * @return A Socket connected to the given host and port. - * @throws UnknownHostException If the hostname cannot be resolved. - * @throws IOException If an I/O error occurs while creating the Socket. - ***/ - @Override public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) - throws UnknownHostException, IOException { - if (connProxy != null) { - Socket s = new Socket(connProxy); - s.bind(new InetSocketAddress(localAddr, localPort)); - s.connect(new InetSocketAddress(host, port)); - return s; - } - return new Socket(host, port, localAddr, localPort); - } - - /*** - * Creates a Socket connected to the given host and port and - * originating from the specified local address and port. - * - * @param address The address of the host to connect to. - * @param port The port to connect to. - * @param localAddr The local address to use. - * @param localPort The local port to use. - * @return A Socket connected to the given host and port. - * @throws IOException If an I/O error occurs while creating the Socket. - ***/ - @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddr, - int localPort) throws IOException { - if (connProxy != null) { - Socket s = new Socket(connProxy); - s.bind(new InetSocketAddress(localAddr, localPort)); - s.connect(new InetSocketAddress(address, port)); - return s; - } - return new Socket(address, port, localAddr, localPort); - } - - /*** - * Creates a ServerSocket bound to a specified port. A port - * of 0 will create the ServerSocket on a system-determined free port. - * - * @param port The port on which to listen, or 0 to use any free port. - * @return A ServerSocket that will listen on a specified port. - * @throws IOException If an I/O error occurs while creating - * the ServerSocket. - ***/ - public ServerSocket createServerSocket(int port) throws IOException { - return new ServerSocket(port); - } - - /*** - * Creates a ServerSocket bound to a specified port with a given - * maximum queue length for incoming connections. A port of 0 will - * create the ServerSocket on a system-determined free port. - * - * @param port The port on which to listen, or 0 to use any free port. - * @param backlog The maximum length of the queue for incoming connections. - * @return A ServerSocket that will listen on a specified port. - * @throws IOException If an I/O error occurs while creating - * the ServerSocket. - ***/ - public ServerSocket createServerSocket(int port, int backlog) throws IOException { - return new ServerSocket(port, backlog); - } - - /*** - * Creates a ServerSocket bound to a specified port on a given local - * address with a given maximum queue length for incoming connections. - * A port of 0 will - * create the ServerSocket on a system-determined free port. - * - * @param port The port on which to listen, or 0 to use any free port. - * @param backlog The maximum length of the queue for incoming connections. - * @param bindAddr The local address to which the ServerSocket should bind. - * @return A ServerSocket that will listen on a specified port. - * @throws IOException If an I/O error occurs while creating - * the ServerSocket. - ***/ - public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) - throws IOException { - return new ServerSocket(port, backlog, bindAddr); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java deleted file mode 100644 index c63818fc..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/MalformedServerReplyException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.io.IOException; - -/*** - * This exception is used to indicate that the reply from a server - * could not be interpreted. Most of the NetComponents classes attempt - * to be as lenient as possible when receiving server replies. Many - * server implementations deviate from IETF protocol specifications, making - * it necessary to be as flexible as possible. However, there will be - * certain situations where it is not possible to continue an operation - * because the server reply could not be interpreted in a meaningful manner. - * In these cases, a MalformedServerReplyException should be thrown. - * - * - ***/ - -public class MalformedServerReplyException extends IOException { - - private static final long serialVersionUID = 6006765264250543945L; - - /*** Constructs a MalformedServerReplyException with no message ***/ - public MalformedServerReplyException() { - super(); - } - - /*** - * Constructs a MalformedServerReplyException with a specified message. - * - * @param message The message explaining the reason for the exception. - ***/ - public MalformedServerReplyException(String message) { - super(message); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/PrintCommandListener.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/PrintCommandListener.java deleted file mode 100644 index 14f92c78..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/PrintCommandListener.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.io.PrintStream; -import java.io.PrintWriter; - -/*** - * This is a support class for some of the example programs. It is - * a sample implementation of the ProtocolCommandListener interface - * which just prints out to a specified stream all command/reply traffic. - * - * @since 2.0 - ***/ - -public class PrintCommandListener implements ProtocolCommandListener { - private final PrintWriter __writer; - private final boolean __nologin; - private final char __eolMarker; - private final boolean __directionMarker; - - /** - * Create the default instance which prints everything. - * - * @param stream where to write the commands and responses - * e.g. System.out - * @since 3.0 - */ - public PrintCommandListener(PrintStream stream) { - this(new PrintWriter(stream)); - } - - /** - * Create an instance which optionally suppresses login command text - * and indicates where the EOL starts with the specified character. - * - * @param stream where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @since 3.0 - */ - public PrintCommandListener(PrintStream stream, boolean suppressLogin) { - this(new PrintWriter(stream), suppressLogin); - } - - /** - * Create an instance which optionally suppresses login command text - * and indicates where the EOL starts with the specified character. - * - * @param stream where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @param eolMarker if non-zero, add a marker just before the EOL. - * @since 3.0 - */ - public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker) { - this(new PrintWriter(stream), suppressLogin, eolMarker); - } - - /** - * Create an instance which optionally suppresses login command text - * and indicates where the EOL starts with the specified character. - * - * @param stream where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @param eolMarker if non-zero, add a marker just before the EOL. - * @param showDirection if {@code true}, add {@code "> "} or {@code "< "} as appropriate to the - * output - * @since 3.0 - */ - public PrintCommandListener(PrintStream stream, boolean suppressLogin, char eolMarker, - boolean showDirection) { - this(new PrintWriter(stream), suppressLogin, eolMarker, showDirection); - } - - /** - * Create the default instance which prints everything. - * - * @param writer where to write the commands and responses - */ - public PrintCommandListener(PrintWriter writer) { - this(writer, false); - } - - /** - * Create an instance which optionally suppresses login command text. - * - * @param writer where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @since 3.0 - */ - public PrintCommandListener(PrintWriter writer, boolean suppressLogin) { - this(writer, suppressLogin, (char) 0); - } - - /** - * Create an instance which optionally suppresses login command text - * and indicates where the EOL starts with the specified character. - * - * @param writer where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @param eolMarker if non-zero, add a marker just before the EOL. - * @since 3.0 - */ - public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker) { - this(writer, suppressLogin, eolMarker, false); - } - - /** - * Create an instance which optionally suppresses login command text - * and indicates where the EOL starts with the specified character. - * - * @param writer where to write the commands and responses - * @param suppressLogin if {@code true}, only print command name for login - * @param eolMarker if non-zero, add a marker just before the EOL. - * @param showDirection if {@code true}, add {@code ">} " or {@code "< "} as appropriate to the - * output - * @since 3.0 - */ - public PrintCommandListener(PrintWriter writer, boolean suppressLogin, char eolMarker, - boolean showDirection) { - __writer = writer; - __nologin = suppressLogin; - __eolMarker = eolMarker; - __directionMarker = showDirection; - } - - @Override public void protocolCommandSent(ProtocolCommandEvent event) { - if (__directionMarker) { - __writer.print("> "); - } - if (__nologin) { - String cmd = event.getCommand(); - if ("PASS".equalsIgnoreCase(cmd) || "USER".equalsIgnoreCase(cmd)) { - __writer.print(cmd); - __writer.println(" *******"); // Don't bother with EOL marker for this! - } else { - final String IMAP_LOGIN = "LOGIN"; - if (IMAP_LOGIN.equalsIgnoreCase(cmd)) { // IMAP - String msg = event.getMessage(); - msg = msg.substring(0, msg.indexOf(IMAP_LOGIN) + IMAP_LOGIN.length()); - __writer.print(msg); - __writer.println(" *******"); // Don't bother with EOL marker for this! - } else { - __writer.print(getPrintableString(event.getMessage())); - } - } - } else { - __writer.print(getPrintableString(event.getMessage())); - } - __writer.flush(); - } - - private String getPrintableString(String msg) { - if (__eolMarker == 0) { - return msg; - } - int pos = msg.indexOf(SocketClient.NETASCII_EOL); - if (pos > 0) { - StringBuilder sb = new StringBuilder(); - sb.append(msg.substring(0, pos)); - sb.append(__eolMarker); - sb.append(msg.substring(pos)); - return sb.toString(); - } - return msg; - } - - @Override public void protocolReplyReceived(ProtocolCommandEvent event) { - if (__directionMarker) { - __writer.print("< "); - } - __writer.print(event.getMessage()); - __writer.flush(); - } -} - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java deleted file mode 100644 index 9f0154e7..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandEvent.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.util.EventObject; - -/*** - * There exists a large class of IETF protocols that work by sending an - * ASCII text command and arguments to a server, and then receiving an - * ASCII text reply. For debugging and other purposes, it is extremely - * useful to log or keep track of the contents of the protocol messages. - * The ProtocolCommandEvent class coupled with the - * {@link ProtocolCommandListener} - * interface facilitate this process. - * - * - * @see ProtocolCommandListener - * @see ProtocolCommandSupport - ***/ - -public class ProtocolCommandEvent extends EventObject { - private static final long serialVersionUID = 403743538418947240L; - - private final int __replyCode; - private final boolean __isCommand; - private final String __message, __command; - - /*** - * Creates a ProtocolCommandEvent signalling a command was sent to - * the server. ProtocolCommandEvents created with this constructor - * should only be sent after a command has been sent, but before the - * reply has been received. - * - * @param source The source of the event. - * @param command The string representation of the command type sent, not - * including the arguments (e.g., "STAT" or "GET"). - * @param message The entire command string verbatim as sent to the server, - * including all arguments. - ***/ - public ProtocolCommandEvent(Object source, String command, String message) { - super(source); - __replyCode = 0; - __message = message; - __isCommand = true; - __command = command; - } - - /*** - * Creates a ProtocolCommandEvent signalling a reply to a command was - * received. ProtocolCommandEvents created with this constructor - * should only be sent after a complete command reply has been received - * fromt a server. - * - * @param source The source of the event. - * @param replyCode The integer code indicating the natureof the reply. - * This will be the protocol integer value for protocols - * that use integer reply codes, or the reply class constant - * corresponding to the reply for protocols like POP3 that use - * strings like OK rather than integer codes (i.e., POP3Repy.OK). - * @param message The entire reply as received from the server. - ***/ - public ProtocolCommandEvent(Object source, int replyCode, String message) { - super(source); - __replyCode = replyCode; - __message = message; - __isCommand = false; - __command = null; - } - - /*** - * Returns the string representation of the command type sent (e.g., "STAT" - * or "GET"). If the ProtocolCommandEvent is a reply event, then null - * is returned. - * - * @return The string representation of the command type sent, or null - * if this is a reply event. - ***/ - public String getCommand() { - return __command; - } - - /*** - * Returns the reply code of the received server reply. Undefined if - * this is not a reply event. - * - * @return The reply code of the received server reply. Undefined if - * not a reply event. - ***/ - public int getReplyCode() { - return __replyCode; - } - - /*** - * Returns true if the ProtocolCommandEvent was generated as a result - * of sending a command. - * - * @return true If the ProtocolCommandEvent was generated as a result - * of sending a command. False otherwise. - ***/ - public boolean isCommand() { - return __isCommand; - } - - /*** - * Returns true if the ProtocolCommandEvent was generated as a result - * of receiving a reply. - * - * @return true If the ProtocolCommandEvent was generated as a result - * of receiving a reply. False otherwise. - ***/ - public boolean isReply() { - return !isCommand(); - } - - /*** - * Returns the entire message sent to or received from the server. - * Includes the line terminator. - * - * @return The entire message sent to or received from the server. - ***/ - public String getMessage() { - return __message; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java deleted file mode 100644 index 12f8e00e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandListener.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import aria.apache.commons.net.ftp.FTPClient; -import java.util.EventListener; - -/*** - * There exists a large class of IETF protocols that work by sending an - * ASCII text command and arguments to a server, and then receiving an - * ASCII text reply. For debugging and other purposes, it is extremely - * useful to log or keep track of the contents of the protocol messages. - * The ProtocolCommandListener interface coupled with the - * {@link ProtocolCommandEvent} class facilitate this process. - *

- * To receive ProtocolCommandEvents, you merely implement the - * ProtocolCommandListener interface and register the class as a listener - * with a ProtocolCommandEvent source such as - * {@link FTPClient}. - * - * - * @see ProtocolCommandEvent - * @see ProtocolCommandSupport - ***/ - -public interface ProtocolCommandListener extends EventListener { - - /*** - * This method is invoked by a ProtocolCommandEvent source after - * sending a protocol command to a server. - * - * @param event The ProtocolCommandEvent fired. - ***/ - public void protocolCommandSent(ProtocolCommandEvent event); - - /*** - * This method is invoked by a ProtocolCommandEvent source after - * receiving a reply from a server. - * - * @param event The ProtocolCommandEvent fired. - ***/ - public void protocolReplyReceived(ProtocolCommandEvent event); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java deleted file mode 100644 index 01b1888e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ProtocolCommandSupport.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.io.Serializable; -import java.util.EventListener; - -import aria.apache.commons.net.util.ListenerList; - -/*** - * ProtocolCommandSupport is a convenience class for managing a list of - * ProtocolCommandListeners and firing ProtocolCommandEvents. You can - * simply delegate ProtocolCommandEvent firing and listener - * registering/unregistering tasks to this class. - * - * - * @see ProtocolCommandEvent - * @see ProtocolCommandListener - ***/ - -public class ProtocolCommandSupport implements Serializable { - private static final long serialVersionUID = -8017692739988399978L; - - private final Object __source; - private final ListenerList __listeners; - - /*** - * Creates a ProtocolCommandSupport instance using the indicated source - * as the source of ProtocolCommandEvents. - * - * @param source The source to use for all generated ProtocolCommandEvents. - ***/ - public ProtocolCommandSupport(Object source) { - __listeners = new ListenerList(); - __source = source; - } - - /*** - * Fires a ProtocolCommandEvent signalling the sending of a command to all - * registered listeners, invoking their - * {@link ProtocolCommandListener#protocolCommandSent protocolCommandSent() } - * methods. - * - * @param command The string representation of the command type sent, not - * including the arguments (e.g., "STAT" or "GET"). - * @param message The entire command string verbatim as sent to the server, - * including all arguments. - ***/ - public void fireCommandSent(String command, String message) { - ProtocolCommandEvent event; - - event = new ProtocolCommandEvent(__source, command, message); - - for (EventListener listener : __listeners) { - ((ProtocolCommandListener) listener).protocolCommandSent(event); - } - } - - /*** - * Fires a ProtocolCommandEvent signalling the reception of a command reply - * to all registered listeners, invoking their - * {@link ProtocolCommandListener#protocolReplyReceived protocolReplyReceived() } - * methods. - * - * @param replyCode The integer code indicating the natureof the reply. - * This will be the protocol integer value for protocols - * that use integer reply codes, or the reply class constant - * corresponding to the reply for protocols like POP3 that use - * strings like OK rather than integer codes (i.e., POP3Repy.OK). - * @param message The entire reply as received from the server. - ***/ - public void fireReplyReceived(int replyCode, String message) { - ProtocolCommandEvent event; - event = new ProtocolCommandEvent(__source, replyCode, message); - - for (EventListener listener : __listeners) { - ((ProtocolCommandListener) listener).protocolReplyReceived(event); - } - } - - /*** - * Adds a ProtocolCommandListener. - * - * @param listener The ProtocolCommandListener to add. - ***/ - public void addProtocolCommandListener(ProtocolCommandListener listener) { - __listeners.addListener(listener); - } - - /*** - * Removes a ProtocolCommandListener. - * - * @param listener The ProtocolCommandListener to remove. - ***/ - public void removeProtocolCommandListener(ProtocolCommandListener listener) { - __listeners.removeListener(listener); - } - - /*** - * Returns the number of ProtocolCommandListeners currently registered. - * - * @return The number of ProtocolCommandListeners currently registered. - ***/ - public int getListenerCount() { - return __listeners.getListenerCount(); - } -} - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/SocketClient.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/SocketClient.java deleted file mode 100644 index 0ac183a7..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/SocketClient.java +++ /dev/null @@ -1,869 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.Socket; -import java.net.SocketException; -import java.nio.charset.Charset; - -import javax.net.ServerSocketFactory; -import javax.net.SocketFactory; - -/** - * The SocketClient provides the basic operations that are required of - * client objects accessing sockets. It is meant to be - * subclassed to avoid having to rewrite the same code over and over again - * to open a socket, close a socket, set timeouts, etc. Of special note - * is the {@link #setSocketFactory setSocketFactory } - * method, which allows you to control the type of Socket the SocketClient - * creates for initiating network connections. This is especially useful - * for adding SSL or proxy support as well as better support for applets. For - * example, you could create a - * {@link SocketFactory} that - * requests browser security capabilities before creating a socket. - * All classes derived from SocketClient should use the - * {@link #_socketFactory_ _socketFactory_ } member variable to - * create Socket and ServerSocket instances rather than instantiating - * them by directly invoking a constructor. By honoring this contract - * you guarantee that a user will always be able to provide his own - * Socket implementations by substituting his own SocketFactory. - * - * @see SocketFactory - */ -public abstract class SocketClient { - /** - * The end of line character sequence used by most IETF protocols. That - * is a carriage return followed by a newline: "\r\n" - */ - public static final String NETASCII_EOL = "\r\n"; - - /** The default SocketFactory shared by all SocketClient instances. */ - private static final SocketFactory __DEFAULT_SOCKET_FACTORY = SocketFactory.getDefault(); - - /** The default {@link ServerSocketFactory} */ - private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = - ServerSocketFactory.getDefault(); - - /** - * A ProtocolCommandSupport object used to manage the registering of - * ProtocolCommandListeners and the firing of ProtocolCommandEvents. - */ - private ProtocolCommandSupport __commandSupport; - - /** The timeout to use after opening a socket. */ - protected int _timeout_; - - /** The socket used for the connection. */ - protected Socket _socket_; - - /** The hostname used for the connection (null = no hostname supplied). */ - protected String _hostname_; - - /** The default port the client should connect to. */ - protected int _defaultPort_; - - /** The socket's InputStream. */ - protected InputStream _input_; - - /** The socket's OutputStream. */ - protected OutputStream _output_; - - /** The socket's SocketFactory. */ - protected SocketFactory _socketFactory_; - - /** The socket's ServerSocket Factory. */ - protected ServerSocketFactory _serverSocketFactory_; - - /** The socket's connect timeout (0 = infinite timeout) */ - private static final int DEFAULT_CONNECT_TIMEOUT = 0; - protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; - - /** Hint for SO_RCVBUF size */ - private int receiveBufferSize = -1; - - /** Hint for SO_SNDBUF size */ - private int sendBufferSize = -1; - - /** The proxy to use when connecting. */ - private Proxy connProxy; - - /** - * Charset to use for byte IO. - */ - private Charset charset = Charset.defaultCharset(); - - /** - * Default constructor for SocketClient. Initializes - * _socket_ to null, _timeout_ to 0, _defaultPort to 0, - * _isConnected_ to false, charset to {@code Charset.defaultCharset()} - * and _socketFactory_ to a shared instance of - * {@link DefaultSocketFactory}. - */ - public SocketClient() { - _socket_ = null; - _hostname_ = null; - _input_ = null; - _output_ = null; - _timeout_ = 0; - _defaultPort_ = 0; - _socketFactory_ = __DEFAULT_SOCKET_FACTORY; - _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; - } - - /** - * Because there are so many connect() methods, the _connectAction_() - * method is provided as a means of performing some action immediately - * after establishing a connection, rather than reimplementing all - * of the connect() methods. The last action performed by every - * connect() method after opening a socket is to call this method. - *

- * This method sets the timeout on the just opened socket to the default - * timeout set by {@link #setDefaultTimeout setDefaultTimeout() }, - * sets _input_ and _output_ to the socket's InputStream and OutputStream - * respectively, and sets _isConnected_ to true. - *

- * Subclasses overriding this method should start by calling - * super._connectAction_() first to ensure the - * initialization of the aforementioned protected variables. - * - * @throws IOException (SocketException) if a problem occurs with the socket - */ - protected void _connectAction_() throws IOException { - _socket_.setSoTimeout(_timeout_); - _input_ = _socket_.getInputStream(); - _output_ = _socket_.getOutputStream(); - } - - /** - * Opens a Socket connected to a remote host at the specified port and - * originating from the current host at a system assigned port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param host The remote host. - * @param port The port to connect to on the remote host. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - */ - public void connect(InetAddress host, int port) throws SocketException, IOException { - _hostname_ = null; - _connect(host, port, null, -1); - } - - /** - * Opens a Socket connected to a remote host at the specified port and - * originating from the current host at a system assigned port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param hostname The name of the remote host. - * @param port The port to connect to on the remote host. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - * @throws java.net.UnknownHostException If the hostname cannot be resolved. - */ - public void connect(String hostname, int port) throws SocketException, IOException { - _hostname_ = hostname; - _connect(InetAddress.getByName(hostname), port, null, -1); - } - - /** - * Opens a Socket connected to a remote host at the specified port and - * originating from the specified local address and port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param host The remote host. - * @param port The port to connect to on the remote host. - * @param localAddr The local address to use. - * @param localPort The local port to use. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - */ - public void connect(InetAddress host, int port, InetAddress localAddr, int localPort) - throws SocketException, IOException { - _hostname_ = null; - _connect(host, port, localAddr, localPort); - } - - // helper method to allow code to be shared with connect(String,...) methods - private void _connect(InetAddress host, int port, InetAddress localAddr, int localPort) - throws SocketException, IOException { - _socket_ = _socketFactory_.createSocket(); - if (receiveBufferSize != -1) { - _socket_.setReceiveBufferSize(receiveBufferSize); - } - if (sendBufferSize != -1) { - _socket_.setSendBufferSize(sendBufferSize); - } - if (localAddr != null) { - _socket_.bind(new InetSocketAddress(localAddr, localPort)); - } - _socket_.connect(new InetSocketAddress(host, port), connectTimeout); - _connectAction_(); - } - - /** - * Opens a Socket connected to a remote host at the specified port and - * originating from the specified local address and port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param hostname The name of the remote host. - * @param port The port to connect to on the remote host. - * @param localAddr The local address to use. - * @param localPort The local port to use. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - * @throws java.net.UnknownHostException If the hostname cannot be resolved. - */ - public void connect(String hostname, int port, InetAddress localAddr, int localPort) - throws SocketException, IOException { - _hostname_ = hostname; - _connect(InetAddress.getByName(hostname), port, localAddr, localPort); - } - - /** - * Opens a Socket connected to a remote host at the current default port - * and originating from the current host at a system assigned port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param host The remote host. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - */ - public void connect(InetAddress host) throws SocketException, IOException { - _hostname_ = null; - connect(host, _defaultPort_); - } - - /** - * Opens a Socket connected to a remote host at the current default - * port and originating from the current host at a system assigned port. - * Before returning, {@link #_connectAction_ _connectAction_() } - * is called to perform connection initialization actions. - *

- * - * @param hostname The name of the remote host. - * @throws SocketException If the socket timeout could not be set. - * @throws IOException If the socket could not be opened. In most - * cases you will only want to catch IOException since SocketException is - * derived from it. - * @throws java.net.UnknownHostException If the hostname cannot be resolved. - */ - public void connect(String hostname) throws SocketException, IOException { - connect(hostname, _defaultPort_); - } - - /** - * Disconnects the socket connection. - * You should call this method after you've finished using the class - * instance and also before you call - * {@link #connect connect() } - * again. _isConnected_ is set to false, _socket_ is set to null, - * _input_ is set to null, and _output_ is set to null. - *

- * - * @throws IOException If there is an error closing the socket. - */ - public void disconnect() throws IOException { - closeQuietly(_socket_); - closeQuietly(_input_); - closeQuietly(_output_); - _socket_ = null; - _hostname_ = null; - _input_ = null; - _output_ = null; - } - - private void closeQuietly(Socket socket) { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - // Ignored - } - } - } - - private void closeQuietly(Closeable close) { - if (close != null) { - try { - close.close(); - } catch (IOException e) { - // Ignored - } - } - } - - /** - * Returns true if the client is currently connected to a server. - *

- * Delegates to {@link Socket#isConnected()} - * - * @return True if the client is currently connected to a server, - * false otherwise. - */ - public boolean isConnected() { - if (_socket_ == null) { - return false; - } - - return _socket_.isConnected(); - } - - /** - * Make various checks on the socket to test if it is available for use. - * Note that the only sure test is to use it, but these checks may help - * in some cases. - * - * @return {@code true} if the socket appears to be available for use - * @see NET-350 - * @since 3.0 - */ - public boolean isAvailable() { - if (isConnected()) { - try { - if (_socket_.getInetAddress() == null) { - return false; - } - if (_socket_.getPort() == 0) { - return false; - } - if (_socket_.getRemoteSocketAddress() == null) { - return false; - } - if (_socket_.isClosed()) { - return false; - } - /* these aren't exact checks (a Socket can be half-open), - but since we usually require two-way data transfer, - we check these here too: */ - if (_socket_.isInputShutdown()) { - return false; - } - if (_socket_.isOutputShutdown()) { - return false; - } - /* ignore the result, catch exceptions: */ - _socket_.getInputStream(); - _socket_.getOutputStream(); - } catch (IOException ioex) { - return false; - } - return true; - } else { - return false; - } - } - - /** - * Sets the default port the SocketClient should connect to when a port - * is not specified. The {@link #_defaultPort_ _defaultPort_ } - * variable stores this value. If never set, the default port is equal - * to zero. - *

- * - * @param port The default port to set. - */ - public void setDefaultPort(int port) { - _defaultPort_ = port; - } - - /** - * Returns the current value of the default port (stored in - * {@link #_defaultPort_ _defaultPort_ }). - *

- * - * @return The current value of the default port. - */ - public int getDefaultPort() { - return _defaultPort_; - } - - /** - * Set the default timeout in milliseconds to use when opening a socket. - * This value is only used previous to a call to - * {@link #connect connect()} - * and should not be confused with {@link #setSoTimeout setSoTimeout()} - * which operates on an the currently opened socket. _timeout_ contains - * the new timeout value. - *

- * - * @param timeout The timeout in milliseconds to use for the socket - * connection. - */ - public void setDefaultTimeout(int timeout) { - _timeout_ = timeout; - } - - /** - * Returns the default timeout in milliseconds that is used when - * opening a socket. - *

- * - * @return The default timeout in milliseconds that is used when - * opening a socket. - */ - public int getDefaultTimeout() { - return _timeout_; - } - - /** - * Set the timeout in milliseconds of a currently open connection. - * Only call this method after a connection has been opened - * by {@link #connect connect()}. - *

- * To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. - * - * @param timeout The timeout in milliseconds to use for the currently - * open socket connection. - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public void setSoTimeout(int timeout) throws SocketException { - _socket_.setSoTimeout(timeout); - } - - /** - * Set the underlying socket send buffer size. - *

- * - * @param size The size of the buffer in bytes. - * @throws SocketException never thrown, but subclasses might want to do so - * @since 2.0 - */ - public void setSendBufferSize(int size) throws SocketException { - sendBufferSize = size; - } - - /** - * Get the current sendBuffer size - * - * @return the size, or -1 if not initialised - * @since 3.0 - */ - protected int getSendBufferSize() { - return sendBufferSize; - } - - /** - * Sets the underlying socket receive buffer size. - *

- * - * @param size The size of the buffer in bytes. - * @throws SocketException never (but subclasses may wish to do so) - * @since 2.0 - */ - public void setReceiveBufferSize(int size) throws SocketException { - receiveBufferSize = size; - } - - /** - * Get the current receivedBuffer size - * - * @return the size, or -1 if not initialised - * @since 3.0 - */ - protected int getReceiveBufferSize() { - return receiveBufferSize; - } - - /** - * Returns the timeout in milliseconds of the currently opened socket. - *

- * - * @return The timeout in milliseconds of the currently opened socket. - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public int getSoTimeout() throws SocketException { - return _socket_.getSoTimeout(); - } - - /** - * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the - * currently opened socket. - *

- * - * @param on True if Nagle's algorithm is to be enabled, false if not. - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public void setTcpNoDelay(boolean on) throws SocketException { - _socket_.setTcpNoDelay(on); - } - - /** - * Returns true if Nagle's algorithm is enabled on the currently opened - * socket. - *

- * - * @return True if Nagle's algorithm is enabled on the currently opened - * socket, false otherwise. - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public boolean getTcpNoDelay() throws SocketException { - return _socket_.getTcpNoDelay(); - } - - /** - * Sets the SO_KEEPALIVE flag on the currently opened socket. - * - * From the Javadocs, the default keepalive time is 2 hours (although this is - * implementation dependent). It looks as though the Windows WSA sockets implementation - * allows a specific keepalive value to be set, although this seems not to be the case on - * other systems. - * - * @param keepAlive If true, keepAlive is turned on - * @throws SocketException if there is a problem with the socket - * @throws NullPointerException if the socket is not currently open - * @since 2.2 - */ - public void setKeepAlive(boolean keepAlive) throws SocketException { - _socket_.setKeepAlive(keepAlive); - } - - /** - * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. - * Delegates to {@link Socket#getKeepAlive()} - * - * @return True if SO_KEEPALIVE is enabled. - * @throws SocketException if there is a problem with the socket - * @throws NullPointerException if the socket is not currently open - * @since 2.2 - */ - public boolean getKeepAlive() throws SocketException { - return _socket_.getKeepAlive(); - } - - /** - * Sets the SO_LINGER timeout on the currently opened socket. - *

- * - * @param on True if linger is to be enabled, false if not. - * @param val The linger timeout (in hundredths of a second?) - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public void setSoLinger(boolean on, int val) throws SocketException { - _socket_.setSoLinger(on, val); - } - - /** - * Returns the current SO_LINGER timeout of the currently opened socket. - *

- * - * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns - * -1. - * @throws SocketException If the operation fails. - * @throws NullPointerException if the socket is not currently open - */ - public int getSoLinger() throws SocketException { - return _socket_.getSoLinger(); - } - - /** - * Returns the port number of the open socket on the local host used - * for the connection. - * Delegates to {@link Socket#getLocalPort()} - *

- * - * @return The port number of the open socket on the local host used - * for the connection. - * @throws NullPointerException if the socket is not currently open - */ - public int getLocalPort() { - return _socket_.getLocalPort(); - } - - /** - * Returns the local address to which the client's socket is bound. - * Delegates to {@link Socket#getLocalAddress()} - *

- * - * @return The local address to which the client's socket is bound. - * @throws NullPointerException if the socket is not currently open - */ - public InetAddress getLocalAddress() { - return _socket_.getLocalAddress(); - } - - /** - * Returns the port number of the remote host to which the client is - * connected. - * Delegates to {@link Socket#getPort()} - *

- * - * @return The port number of the remote host to which the client is - * connected. - * @throws NullPointerException if the socket is not currently open - */ - public int getRemotePort() { - return _socket_.getPort(); - } - - /** - * @return The remote address to which the client is connected. - * Delegates to {@link Socket#getInetAddress()} - * @throws NullPointerException if the socket is not currently open - */ - public InetAddress getRemoteAddress() { - return _socket_.getInetAddress(); - } - - /** - * Verifies that the remote end of the given socket is connected to the - * the same host that the SocketClient is currently connected to. This - * is useful for doing a quick security check when a client needs to - * accept a connection from a server, such as an FTP data connection or - * a BSD R command standard error stream. - *

- * - * @param socket the item to check against - * @return True if the remote hosts are the same, false if not. - */ - public boolean verifyRemote(Socket socket) { - InetAddress host1, host2; - - host1 = socket.getInetAddress(); - host2 = getRemoteAddress(); - - return host1.equals(host2); - } - - /** - * Sets the SocketFactory used by the SocketClient to open socket - * connections. If the factory value is null, then a default - * factory is used (only do this to reset the factory after having - * previously altered it). - * Any proxy setting is discarded. - *

- * - * @param factory The new SocketFactory the SocketClient should use. - */ - public void setSocketFactory(SocketFactory factory) { - if (factory == null) { - _socketFactory_ = __DEFAULT_SOCKET_FACTORY; - } else { - _socketFactory_ = factory; - } - // re-setting the socket factory makes the proxy setting useless, - // so set the field to null so that getProxy() doesn't return a - // Proxy that we're actually not using. - connProxy = null; - } - - /** - * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket - * connections. If the factory value is null, then a default - * factory is used (only do this to reset the factory after having - * previously altered it). - *

- * - * @param factory The new ServerSocketFactory the SocketClient should use. - * @since 2.0 - */ - public void setServerSocketFactory(ServerSocketFactory factory) { - if (factory == null) { - _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; - } else { - _serverSocketFactory_ = factory; - } - } - - /** - * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} - * object's - * connect() method. - * - * @param connectTimeout The connection timeout to use (in ms) - * @since 2.0 - */ - public void setConnectTimeout(int connectTimeout) { - this.connectTimeout = connectTimeout; - } - - /** - * Get the underlying socket connection timeout. - * - * @return timeout (in ms) - * @since 2.0 - */ - public int getConnectTimeout() { - return connectTimeout; - } - - /** - * Get the underlying {@link ServerSocketFactory} - * - * @return The server socket factory - * @since 2.2 - */ - public ServerSocketFactory getServerSocketFactory() { - return _serverSocketFactory_; - } - - /** - * Adds a ProtocolCommandListener. - * - * @param listener The ProtocolCommandListener to add. - * @since 3.0 - */ - public void addProtocolCommandListener(ProtocolCommandListener listener) { - getCommandSupport().addProtocolCommandListener(listener); - } - - /** - * Removes a ProtocolCommandListener. - * - * @param listener The ProtocolCommandListener to remove. - * @since 3.0 - */ - public void removeProtocolCommandListener(ProtocolCommandListener listener) { - getCommandSupport().removeProtocolCommandListener(listener); - } - - /** - * If there are any listeners, send them the reply details. - * - * @param replyCode the code extracted from the reply - * @param reply the full reply text - * @since 3.0 - */ - protected void fireReplyReceived(int replyCode, String reply) { - if (getCommandSupport().getListenerCount() > 0) { - getCommandSupport().fireReplyReceived(replyCode, reply); - } - } - - /** - * If there are any listeners, send them the command details. - * - * @param command the command name - * @param message the complete message, including command name - * @since 3.0 - */ - protected void fireCommandSent(String command, String message) { - if (getCommandSupport().getListenerCount() > 0) { - getCommandSupport().fireCommandSent(command, message); - } - } - - /** - * Create the CommandSupport instance if required - */ - protected void createCommandSupport() { - __commandSupport = new ProtocolCommandSupport(this); - } - - /** - * Subclasses can override this if they need to provide their own - * instance field for backwards compatibilty. - * - * @return the CommandSupport instance, may be {@code null} - * @since 3.0 - */ - protected ProtocolCommandSupport getCommandSupport() { - return __commandSupport; - } - - /** - * Sets the proxy for use with all the connections. - * The proxy is used for connections established after the - * call to this method. - * - * @param proxy the new proxy for connections. - * @since 3.2 - */ - public void setProxy(Proxy proxy) { - setSocketFactory(new DefaultSocketFactory(proxy)); - connProxy = proxy; - } - - /** - * Gets the proxy for use with all the connections. - * - * @return the current proxy for connections. - */ - public Proxy getProxy() { - return connProxy; - } - - /** - * Gets the charset name. - * - * @return the charset. - * @since 3.3 - * @deprecated Since the code now requires Java 1.6 as a mininmum - */ - @Deprecated public String getCharsetName() { - return charset.name(); - } - - /** - * Gets the charset. - * - * @return the charset. - * @since 3.3 - */ - public Charset getCharset() { - return charset; - } - - /** - * Sets the charset. - * - * @param charset the charset. - * @since 3.3 - */ - public void setCharset(Charset charset) { - this.charset = charset; - } - - /* - * N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, - * so the abstract method is needed to pass the instance to the methods which were moved here. - */ -} - - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/Configurable.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/Configurable.java deleted file mode 100644 index b0361f80..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/Configurable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -/** - * This interface adds the aspect of configurability by means of - * a supplied FTPClientConfig object to other classes in the - * system, especially listing parsers. - */ -public interface Configurable { - - /** - * @param config the object containing the configuration data - * @throws IllegalArgumentException if the elements of the - * config are somehow inadequate to configure the - * Configurable object. - */ - public void configure(FTPClientConfig config); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTP.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTP.java deleted file mode 100644 index 12a99b15..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTP.java +++ /dev/null @@ -1,1777 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.util.ArrayList; - -import aria.apache.commons.net.MalformedServerReplyException; -import aria.apache.commons.net.ProtocolCommandSupport; -import aria.apache.commons.net.SocketClient; -import aria.apache.commons.net.io.CRLFLineReader; - -/*** - * FTP provides the basic the functionality necessary to implement your - * own FTP client. It extends SocketClient since - * extending TelnetClient was causing unwanted behavior (like connections - * that did not time out properly). - *

- * To derive the full benefits of the FTP class requires some knowledge - * of the FTP protocol defined in RFC 959. However, there is no reason - * why you should have to use the FTP class. The - * {@link FTPClient} class, - * derived from FTP, - * implements all the functionality required of an FTP client. The - * FTP class is made public to provide access to various FTP constants - * and to make it easier for adventurous programmers (or those with - * special needs) to interact with the FTP protocol and implement their - * own clients. A set of methods with names corresponding to the FTP - * command names are provided to facilitate this interaction. - *

- * You should keep in mind that the FTP server may choose to prematurely - * close a connection if the client has been idle for longer than a - * given time period (usually 900 seconds). The FTP class will detect a - * premature FTP server connection closing when it receives a - * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } - * response to a command. - * When that occurs, the FTP class method encountering that reply will throw - * an {@link FTPConnectionClosedException} - * . FTPConectionClosedException - * is a subclass of IOException and therefore need not be - * caught separately, but if you are going to catch it separately, its - * catch block must appear before the more general IOException - * catch block. When you encounter an - * {@link FTPConnectionClosedException} - * , you must disconnect the connection with - * {@link #disconnect disconnect() } to properly clean up the - * system resources used by FTP. Before disconnecting, you may check the - * last reply code and text with - * {@link #getReplyCode getReplyCode }, - * {@link #getReplyString getReplyString }, - * and {@link #getReplyStrings getReplyStrings}. - * You may avoid server disconnections while the client is idle by - * periodicaly sending NOOP commands to the server. - *

- * Rather than list it separately for each method, we mention here that - * every method communicating with the server and throwing an IOException - * can also throw a - * {@link MalformedServerReplyException} - * , which is a subclass - * of IOException. A MalformedServerReplyException will be thrown when - * the reply received from the server deviates enough from the protocol - * specification that it cannot be interpreted in a useful manner despite - * attempts to be as lenient as possible. - * - * @see FTPClient - * @see FTPConnectionClosedException - * @see MalformedServerReplyException - * @version $Id: FTP.java 1782546 2017-02-11 00:05:41Z sebb $ - ***/ - -public class FTP extends SocketClient { - /*** The default FTP data port (20). ***/ - public static final int DEFAULT_DATA_PORT = 20; - /*** The default FTP control port (21). ***/ - public static final int DEFAULT_PORT = 21; - - /*** - * A constant used to indicate the file(s) being transferred should - * be treated as ASCII. This is the default file type. All constants - * ending in FILE_TYPE are used to indicate file types. - ***/ - public static final int ASCII_FILE_TYPE = 0; - - /*** - * A constant used to indicate the file(s) being transferred should - * be treated as EBCDIC. Note however that there are several different - * EBCDIC formats. All constants ending in FILE_TYPE - * are used to indicate file types. - ***/ - public static final int EBCDIC_FILE_TYPE = 1; - - /*** - * A constant used to indicate the file(s) being transferred should - * be treated as a binary image, i.e., no translations should be - * performed. All constants ending in FILE_TYPE are used to - * indicate file types. - ***/ - public static final int BINARY_FILE_TYPE = 2; - - /*** - * A constant used to indicate the file(s) being transferred should - * be treated as a local type. All constants ending in - * FILE_TYPE are used to indicate file types. - ***/ - public static final int LOCAL_FILE_TYPE = 3; - - /*** - * A constant used for text files to indicate a non-print text format. - * This is the default format. - * All constants ending in TEXT_FORMAT are used to indicate - * text formatting for text transfers (both ASCII and EBCDIC). - ***/ - public static final int NON_PRINT_TEXT_FORMAT = 4; - - /*** - * A constant used to indicate a text file contains format vertical format - * control characters. - * All constants ending in TEXT_FORMAT are used to indicate - * text formatting for text transfers (both ASCII and EBCDIC). - ***/ - public static final int TELNET_TEXT_FORMAT = 5; - - /*** - * A constant used to indicate a text file contains ASA vertical format - * control characters. - * All constants ending in TEXT_FORMAT are used to indicate - * text formatting for text transfers (both ASCII and EBCDIC). - ***/ - public static final int CARRIAGE_CONTROL_TEXT_FORMAT = 6; - - /*** - * A constant used to indicate a file is to be treated as a continuous - * sequence of bytes. This is the default structure. All constants ending - * in _STRUCTURE are used to indicate file structure for - * file transfers. - ***/ - public static final int FILE_STRUCTURE = 7; - - /*** - * A constant used to indicate a file is to be treated as a sequence - * of records. All constants ending in _STRUCTURE - * are used to indicate file structure for file transfers. - ***/ - public static final int RECORD_STRUCTURE = 8; - - /*** - * A constant used to indicate a file is to be treated as a set of - * independent indexed pages. All constants ending in - * _STRUCTURE are used to indicate file structure for file - * transfers. - ***/ - public static final int PAGE_STRUCTURE = 9; - - /*** - * A constant used to indicate a file is to be transferred as a stream - * of bytes. This is the default transfer mode. All constants ending - * in TRANSFER_MODE are used to indicate file transfer - * modes. - ***/ - public static final int STREAM_TRANSFER_MODE = 10; - - /*** - * A constant used to indicate a file is to be transferred as a series - * of blocks. All constants ending in TRANSFER_MODE are used - * to indicate file transfer modes. - ***/ - public static final int BLOCK_TRANSFER_MODE = 11; - - /*** - * A constant used to indicate a file is to be transferred as FTP - * compressed data. All constants ending in TRANSFER_MODE - * are used to indicate file transfer modes. - ***/ - public static final int COMPRESSED_TRANSFER_MODE = 12; - - // We have to ensure that the protocol communication is in ASCII - // but we use ISO-8859-1 just in case 8-bit characters cross - // the wire. - /** - * The default character encoding used for communicating over an - * FTP control connection. The default encoding is an - * ASCII-compatible encoding. Some FTP servers expect other - * encodings. You can change the encoding used by an FTP instance - * with {@link #setControlEncoding setControlEncoding}. - */ - public static final String DEFAULT_CONTROL_ENCODING = "ISO-8859-1"; - - /** Length of the FTP reply code (3 alphanumerics) */ - public static final int REPLY_CODE_LEN = 3; - - private static final String __modes = "AEILNTCFRPSBC"; - - protected int _replyCode; - protected ArrayList _replyLines; - protected boolean _newReplyString; - protected String _replyString; - protected String _controlEncoding; - - /** - * A ProtocolCommandSupport object used to manage the registering of - * ProtocolCommandListeners and te firing of ProtocolCommandEvents. - */ - protected ProtocolCommandSupport _commandSupport_; - - /** - * This is used to signal whether a block of multiline responses beginning - * with xxx must be terminated by the same numeric code xxx - * See section 4.2 of RFC 959 for details. - */ - protected boolean strictMultilineParsing = false; - - /** - * If this is true, then non-multiline replies must have the format: - * 3 digit code - * If false, then the 3 digit code does not have to be followed by space - * See section 4.2 of RFC 959 for details. - */ - private boolean strictReplyParsing = true; - - /** - * Wraps SocketClient._input_ to facilitate the reading of text - * from the FTP control connection. Do not access the control - * connection via SocketClient._input_. This member starts - * with a null value, is initialized in {@link #_connectAction_}, - * and set to null in {@link #disconnect}. - */ - protected BufferedReader _controlInput_; - - /** - * Wraps SocketClient._output_ to facilitate the writing of text - * to the FTP control connection. Do not access the control - * connection via SocketClient._output_. This member starts - * with a null value, is initialized in {@link #_connectAction_}, - * and set to null in {@link #disconnect}. - */ - protected BufferedWriter _controlOutput_; - - /*** - * The default FTP constructor. Sets the default port to - * DEFAULT_PORT and initializes internal data structures - * for saving FTP reply information. - ***/ - public FTP() { - super(); - setDefaultPort(DEFAULT_PORT); - _replyLines = new ArrayList(); - _newReplyString = false; - _replyString = null; - _controlEncoding = DEFAULT_CONTROL_ENCODING; - _commandSupport_ = new ProtocolCommandSupport(this); - } - - // The RFC-compliant multiline termination check - private boolean __strictCheck(String line, String code) { - return (!(line.startsWith(code) && line.charAt(REPLY_CODE_LEN) == ' ')); - } - - // The strict check is too strong a condition because of non-conforming ftp - // servers like ftp.funet.fi which sent 226 as the last line of a - // 426 multi-line reply in response to ls /. We relax the condition to - // test that the line starts with a digit rather than starting with - // the code. - private boolean __lenientCheck(String line) { - return (!(line.length() > REPLY_CODE_LEN - && line.charAt(REPLY_CODE_LEN) != '-' - && Character.isDigit(line.charAt(0)))); - } - - /** - * Get the reply, and pass it to command listeners - */ - private void __getReply() throws IOException { - __getReply(true); - } - - /** - * Get the reply, but don't pass it to command listeners. - * Used for keep-alive processing only. - * - * @throws IOException on error - * @since 3.0 - */ - protected void __getReplyNoReport() throws IOException { - __getReply(false); - } - - private void __getReply(boolean reportReply) throws IOException { - int length; - - _newReplyString = true; - _replyLines.clear(); - - String line = _controlInput_.readLine(); - - if (line == null) { - throw new FTPConnectionClosedException("Connection closed without indication."); - } - - // In case we run into an anomaly we don't want fatal index exceptions - // to be thrown. - length = line.length(); - if (length < REPLY_CODE_LEN) { - throw new MalformedServerReplyException("Truncated server reply: " + line); - } - - String code = null; - try { - code = line.substring(0, REPLY_CODE_LEN); - _replyCode = Integer.parseInt(code); - } catch (NumberFormatException e) { - throw new MalformedServerReplyException( - "Could not parse response code.\nServer Reply: " + line); - } - - _replyLines.add(line); - - // Check the server reply type - if (length > REPLY_CODE_LEN) { - char sep = line.charAt(REPLY_CODE_LEN); - // Get extra lines if message continues. - if (sep == '-') { - do { - line = _controlInput_.readLine(); - - if (line == null) { - throw new FTPConnectionClosedException("Connection closed without indication."); - } - - _replyLines.add(line); - - // The length() check handles problems that could arise from readLine() - // returning too soon after encountering a naked CR or some other - // anomaly. - } while (isStrictMultilineParsing() ? __strictCheck(line, code) : __lenientCheck(line)); - } else if (isStrictReplyParsing()) { - if (length == REPLY_CODE_LEN + 1) { // expecting some text - throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); - } else if (sep != ' ') { - throw new MalformedServerReplyException("Invalid server reply: '" + line + "'"); - } - } - } else if (isStrictReplyParsing()) { - throw new MalformedServerReplyException("Truncated server reply: '" + line + "'"); - } - - if (reportReply) { - fireReplyReceived(_replyCode, getReplyString()); - } - - if (_replyCode == FTPReply.SERVICE_NOT_AVAILABLE) { - throw new FTPConnectionClosedException( - "FTP response 421 received. Server closed connection."); - } - } - - /** - * Initiates control connections and gets initial reply. - * Initializes {@link #_controlInput_} and {@link #_controlOutput_}. - */ - @Override protected void _connectAction_() throws IOException { - _connectAction_(null); - } - - /** - * Initiates control connections and gets initial reply. - * Initializes {@link #_controlInput_} and {@link #_controlOutput_}. - * - * @param socketIsReader the reader to reuse (if non-null) - * @throws IOException on error - * @since 3.4 - */ - protected void _connectAction_(Reader socketIsReader) throws IOException { - super._connectAction_(); // sets up _input_ and _output_ - if (socketIsReader == null) { - _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); - } else { - _controlInput_ = new CRLFLineReader(socketIsReader); - } - _controlOutput_ = new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); - if (connectTimeout > 0) { // NET-385 - int original = _socket_.getSoTimeout(); - _socket_.setSoTimeout(connectTimeout); - try { - __getReply(); - // If we received code 120, we have to fetch completion reply. - if (FTPReply.isPositivePreliminary(_replyCode)) { - __getReply(); - } - } catch (SocketTimeoutException e) { - IOException ioe = new IOException("Timed out waiting for initial connect reply"); - ioe.initCause(e); - throw ioe; - } finally { - _socket_.setSoTimeout(original); - } - } else { - __getReply(); - // If we received code 120, we have to fetch completion reply. - if (FTPReply.isPositivePreliminary(_replyCode)) { - __getReply(); - } - } - } - - /** - * Saves the character encoding to be used by the FTP control connection. - * Some FTP servers require that commands be issued in a non-ASCII - * encoding like UTF-8 so that filenames with multi-byte character - * representations (e.g, Big 8) can be specified. - *

- * Please note that this has to be set before the connection is established. - * - * @param encoding The new character encoding for the control connection. - */ - public void setControlEncoding(String encoding) { - _controlEncoding = encoding; - } - - /** - * @return The character encoding used to communicate over the - * control connection. - */ - public String getControlEncoding() { - return _controlEncoding; - } - - /*** - * Closes the control connection to the FTP server and sets to null - * some internal data so that the memory may be reclaimed by the - * garbage collector. The reply text and code information from the - * last command is voided so that the memory it used may be reclaimed. - * Also sets {@link #_controlInput_} and {@link #_controlOutput_} to null. - * - * @throws IOException If an error occurs while disconnecting. - ***/ - @Override public void disconnect() throws IOException { - super.disconnect(); - _controlInput_ = null; - _controlOutput_ = null; - _newReplyString = false; - _replyString = null; - } - - /*** - * Sends an FTP command to the server, waits for a reply and returns the - * numerical response code. After invocation, for more detailed - * information, the actual reply text can be accessed by calling - * {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The text representation of the FTP command to send. - * @param args The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int sendCommand(String command, String args) throws IOException { - if (_controlOutput_ == null) { - throw new IOException("Connection is not open"); - } - - final String message = __buildMessage(command, args); - - __send(message); - - fireCommandSent(command, message); - - __getReply(); - return _replyCode; - } - - private String __buildMessage(String command, String args) { - final StringBuilder __commandBuffer = new StringBuilder(); - - __commandBuffer.append(command); - - if (args != null) { - __commandBuffer.append(' '); - __commandBuffer.append(args); - } - __commandBuffer.append(SocketClient.NETASCII_EOL); - return __commandBuffer.toString(); - } - - private void __send(String message) - throws IOException, FTPConnectionClosedException, SocketException { - try { - _controlOutput_.write(message); - _controlOutput_.flush(); - } catch (SocketException e) { - if (!isConnected()) { - throw new FTPConnectionClosedException("Connection unexpectedly closed."); - } else { - throw e; - } - } - } - - /** - * Send a noop and get the reply without reporting to the command listener. - * Intended for use with keep-alive. - * - * @throws IOException on error - * @since 3.0 - */ - protected void __noop() throws IOException { - String msg = __buildMessage(FTPCmd.NOOP.getCommand(), null); - __send(msg); - __getReplyNoReport(); // This may timeout - } - - /*** - * Sends an FTP command to the server, waits for a reply and returns the - * numerical response code. After invocation, for more detailed - * information, the actual reply text can be accessed by calling - * {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The FTPCommand constant corresponding to the FTP command - * to send. - * @param args The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @deprecated (3.3) Use {@link #sendCommand(FTPCmd, String)} instead - ***/ - @Deprecated public int sendCommand(int command, String args) throws IOException { - return sendCommand(FTPCommand.getCommand(command), args); - } - - /** - * Sends an FTP command to the server, waits for a reply and returns the - * numerical response code. After invocation, for more detailed - * information, the actual reply text can be accessed by calling - * {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The FTPCmd enum corresponding to the FTP command - * to send. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.3 - */ - public int sendCommand(FTPCmd command) throws IOException { - return sendCommand(command, null); - } - - /** - * Sends an FTP command to the server, waits for a reply and returns the - * numerical response code. After invocation, for more detailed - * information, the actual reply text can be accessed by calling - * {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The FTPCmd enum corresponding to the FTP command - * to send. - * @param args The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.3 - */ - public int sendCommand(FTPCmd command, String args) throws IOException { - return sendCommand(command.getCommand(), args); - } - - /*** - * Sends an FTP command with no arguments to the server, waits for a - * reply and returns the numerical response code. After invocation, for - * more detailed information, the actual reply text can be accessed by - * calling {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The text representation of the FTP command to send. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int sendCommand(String command) throws IOException { - return sendCommand(command, null); - } - - /*** - * Sends an FTP command with no arguments to the server, waits for a - * reply and returns the numerical response code. After invocation, for - * more detailed information, the actual reply text can be accessed by - * calling {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. - * - * @param command The FTPCommand constant corresponding to the FTP command - * to send. - * @return The integer value of the FTP reply code returned by the server - * in response to the command. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int sendCommand(int command) throws IOException { - return sendCommand(command, null); - } - - /*** - * Returns the integer value of the reply code of the last FTP reply. - * You will usually only use this method after you connect to the - * FTP server to check that the connection was successful since - * connect is of type void. - * - * @return The integer value of the reply code of the last FTP reply. - ***/ - public int getReplyCode() { - return _replyCode; - } - - /*** - * Fetches a reply from the FTP server and returns the integer reply - * code. After calling this method, the actual reply text can be accessed - * from either calling {@link #getReplyString getReplyString } or - * {@link #getReplyStrings getReplyStrings }. Only use this - * method if you are implementing your own FTP client or if you need to - * fetch a secondary response from the FTP server. - * - * @return The integer value of the reply code of the fetched FTP reply. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while receiving the - * server reply. - ***/ - public int getReply() throws IOException { - __getReply(); - return _replyCode; - } - - /*** - * Returns the lines of text from the last FTP server response as an array - * of strings, one entry per line. The end of line markers of each are - * stripped from each line. - * - * @return The lines of text from the last FTP response as an array. - ***/ - public String[] getReplyStrings() { - return _replyLines.toArray(new String[_replyLines.size()]); - } - - /*** - * Returns the entire text of the last FTP server response exactly - * as it was received, including all end of line markers in NETASCII - * format. - * - * @return The entire text from the last FTP response as a String. - ***/ - public String getReplyString() { - StringBuilder buffer; - - if (!_newReplyString) { - return _replyString; - } - - buffer = new StringBuilder(256); - - for (String line : _replyLines) { - buffer.append(line); - buffer.append(SocketClient.NETASCII_EOL); - } - - _newReplyString = false; - - return (_replyString = buffer.toString()); - } - - /*** - * A convenience method to send the FTP USER command to the server, - * receive the reply, and return the reply code. - * - * @param username The username to login under. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int user(String username) throws IOException { - return sendCommand(FTPCmd.USER, username); - } - - /** - * A convenience method to send the FTP PASS command to the server, - * receive the reply, and return the reply code. - * - * @param password The plain text password of the username being logged into. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - */ - public int pass(String password) throws IOException { - return sendCommand(FTPCmd.PASS, password); - } - - /*** - * A convenience method to send the FTP ACCT command to the server, - * receive the reply, and return the reply code. - * - * @param account The account name to access. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int acct(String account) throws IOException { - return sendCommand(FTPCmd.ACCT, account); - } - - /*** - * A convenience method to send the FTP ABOR command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int abor() throws IOException { - return sendCommand(FTPCmd.ABOR); - } - - /*** - * A convenience method to send the FTP CWD command to the server, - * receive the reply, and return the reply code. - * - * @param directory The new working directory. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int cwd(String directory) throws IOException { - return sendCommand(FTPCmd.CWD, directory); - } - - /*** - * A convenience method to send the FTP CDUP command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int cdup() throws IOException { - return sendCommand(FTPCmd.CDUP); - } - - /*** - * A convenience method to send the FTP QUIT command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int quit() throws IOException { - return sendCommand(FTPCmd.QUIT); - } - - /*** - * A convenience method to send the FTP REIN command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int rein() throws IOException { - return sendCommand(FTPCmd.REIN); - } - - /*** - * A convenience method to send the FTP SMNT command to the server, - * receive the reply, and return the reply code. - * - * @param dir The directory name. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int smnt(String dir) throws IOException { - return sendCommand(FTPCmd.SMNT, dir); - } - - /*** - * A convenience method to send the FTP PORT command to the server, - * receive the reply, and return the reply code. - * - * @param host The host owning the port. - * @param port The new port. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int port(InetAddress host, int port) throws IOException { - int num; - StringBuilder info = new StringBuilder(24); - - info.append(host.getHostAddress().replace('.', ',')); - num = port >>> 8; - info.append(','); - info.append(num); - info.append(','); - num = port & 0xff; - info.append(num); - - return sendCommand(FTPCmd.PORT, info.toString()); - } - - /*** - * A convenience method to send the FTP EPRT command to the server, - * receive the reply, and return the reply code. - * - * Examples: - *

    - *
  • EPRT |1|132.235.1.2|6275|
  • - *
  • EPRT |2|1080::8:800:200C:417A|5282|
  • - *
- * - * @see "http://www.faqs.org/rfcs/rfc2428.html" - * - * @param host The host owning the port. - * @param port The new port. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 2.2 - ***/ - public int eprt(InetAddress host, int port) throws IOException { - int num; - StringBuilder info = new StringBuilder(); - String h; - - // If IPv6, trim the zone index - h = host.getHostAddress(); - num = h.indexOf("%"); - if (num > 0) { - h = h.substring(0, num); - } - - info.append("|"); - - if (host instanceof Inet4Address) { - info.append("1"); - } else if (host instanceof Inet6Address) { - info.append("2"); - } - info.append("|"); - info.append(h); - info.append("|"); - info.append(port); - info.append("|"); - - return sendCommand(FTPCmd.EPRT, info.toString()); - } - - /*** - * A convenience method to send the FTP PASV command to the server, - * receive the reply, and return the reply code. Remember, it's up - * to you to interpret the reply string containing the host/port - * information. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int pasv() throws IOException { - return sendCommand(FTPCmd.PASV); - } - - /*** - * A convenience method to send the FTP EPSV command to the server, - * receive the reply, and return the reply code. Remember, it's up - * to you to interpret the reply string containing the host/port - * information. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 2.2 - ***/ - public int epsv() throws IOException { - return sendCommand(FTPCmd.EPSV); - } - - /** - * A convenience method to send the FTP TYPE command for text files - * to the server, receive the reply, and return the reply code. - * - * @param fileType The type of the file (one of the FILE_TYPE - * constants). - * @param formatOrByteSize The format of the file (one of the - * _FORMAT constants. In the case of - * LOCAL_FILE_TYPE, the byte size. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - */ - public int type(int fileType, int formatOrByteSize) throws IOException { - StringBuilder arg = new StringBuilder(); - - arg.append(__modes.charAt(fileType)); - arg.append(' '); - if (fileType == LOCAL_FILE_TYPE) { - arg.append(formatOrByteSize); - } else { - arg.append(__modes.charAt(formatOrByteSize)); - } - - return sendCommand(FTPCmd.TYPE, arg.toString()); - } - - /** - * A convenience method to send the FTP TYPE command to the server, - * receive the reply, and return the reply code. - * - * @param fileType The type of the file (one of the FILE_TYPE - * constants). - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - */ - public int type(int fileType) throws IOException { - return sendCommand(FTPCmd.TYPE, __modes.substring(fileType, fileType + 1)); - } - - /*** - * A convenience method to send the FTP STRU command to the server, - * receive the reply, and return the reply code. - * - * @param structure The structure of the file (one of the - * _STRUCTURE constants). - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int stru(int structure) throws IOException { - return sendCommand(FTPCmd.STRU, __modes.substring(structure, structure + 1)); - } - - /*** - * A convenience method to send the FTP MODE command to the server, - * receive the reply, and return the reply code. - * - * @param mode The transfer mode to use (one of the - * TRANSFER_MODE constants). - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int mode(int mode) throws IOException { - return sendCommand(FTPCmd.MODE, __modes.substring(mode, mode + 1)); - } - - /*** - * A convenience method to send the FTP RETR command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param pathname The pathname of the file to retrieve. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int retr(String pathname) throws IOException { - return sendCommand(FTPCmd.RETR, pathname); - } - - /*** - * A convenience method to send the FTP STOR command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param pathname The pathname to use for the file when stored at - * the remote end of the transfer. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int stor(String pathname) throws IOException { - return sendCommand(FTPCmd.STOR, pathname); - } - - /*** - * A convenience method to send the FTP STOU command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int stou() throws IOException { - return sendCommand(FTPCmd.STOU); - } - - /*** - * A convenience method to send the FTP STOU command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * @param pathname The base pathname to use for the file when stored at - * the remote end of the transfer. Some FTP servers - * require this. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - */ - public int stou(String pathname) throws IOException { - return sendCommand(FTPCmd.STOU, pathname); - } - - /*** - * A convenience method to send the FTP APPE command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param pathname The pathname to use for the file when stored at - * the remote end of the transfer. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int appe(String pathname) throws IOException { - return sendCommand(FTPCmd.APPE, pathname); - } - - /*** - * A convenience method to send the FTP ALLO command to the server, - * receive the reply, and return the reply code. - * - * @param bytes The number of bytes to allocate. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int allo(int bytes) throws IOException { - return sendCommand(FTPCmd.ALLO, Integer.toString(bytes)); - } - - /** - * A convenience method to send the FTP FEAT command to the server, receive the reply, - * and return the reply code. - * - * @return The reply code received by the server - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 2.2 - */ - public int feat() throws IOException { - return sendCommand(FTPCmd.FEAT); - } - - /*** - * A convenience method to send the FTP ALLO command to the server, - * receive the reply, and return the reply code. - * - * @param bytes The number of bytes to allocate. - * @param recordSize The size of a record. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int allo(int bytes, int recordSize) throws IOException { - return sendCommand(FTPCmd.ALLO, Integer.toString(bytes) + " R " + Integer.toString(recordSize)); - } - - /*** - * A convenience method to send the FTP REST command to the server, - * receive the reply, and return the reply code. - * - * @param marker The marker at which to restart a transfer. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int rest(String marker) throws IOException { - return sendCommand(FTPCmd.REST, marker); - } - - /** - * @param file name of file - * @return the status - * @throws IOException on error - * @since 2.0 - **/ - public int mdtm(String file) throws IOException { - return sendCommand(FTPCmd.MDTM, file); - } - - /** - * A convenience method to send the FTP MFMT command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname for which mtime is to be changed - * @param timeval Timestamp in YYYYMMDDhhmmss format - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 - * @since 2.2 - **/ - public int mfmt(String pathname, String timeval) throws IOException { - return sendCommand(FTPCmd.MFMT, timeval + " " + pathname); - } - - /*** - * A convenience method to send the FTP RNFR command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname to rename from. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int rnfr(String pathname) throws IOException { - return sendCommand(FTPCmd.RNFR, pathname); - } - - /*** - * A convenience method to send the FTP RNTO command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname to rename to - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int rnto(String pathname) throws IOException { - return sendCommand(FTPCmd.RNTO, pathname); - } - - /*** - * A convenience method to send the FTP DELE command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname to delete. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int dele(String pathname) throws IOException { - return sendCommand(FTPCmd.DELE, pathname); - } - - /*** - * A convenience method to send the FTP RMD command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname of the directory to remove. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int rmd(String pathname) throws IOException { - return sendCommand(FTPCmd.RMD, pathname); - } - - /*** - * A convenience method to send the FTP MKD command to the server, - * receive the reply, and return the reply code. - * - * @param pathname The pathname of the new directory to create. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int mkd(String pathname) throws IOException { - return sendCommand(FTPCmd.MKD, pathname); - } - - /*** - * A convenience method to send the FTP PWD command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int pwd() throws IOException { - return sendCommand(FTPCmd.PWD); - } - - /*** - * A convenience method to send the FTP LIST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int list() throws IOException { - return sendCommand(FTPCmd.LIST); - } - - /*** - * A convenience method to send the FTP LIST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param pathname The pathname to list, - * may be {@code null} in which case the command is sent with no parameters - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int list(String pathname) throws IOException { - return sendCommand(FTPCmd.LIST, pathname); - } - - /** - * A convenience method to send the FTP MLSD command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.0 - */ - public int mlsd() throws IOException { - return sendCommand(FTPCmd.MLSD); - } - - /** - * A convenience method to send the FTP MLSD command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param path the path to report on - * @return The reply code received from the server, - * may be {@code null} in which case the command is sent with no parameters - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.0 - */ - public int mlsd(String path) throws IOException { - return sendCommand(FTPCmd.MLSD, path); - } - - /** - * A convenience method to send the FTP MLST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.0 - */ - public int mlst() throws IOException { - return sendCommand(FTPCmd.MLST); - } - - /** - * A convenience method to send the FTP MLST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param path the path to report on - * @return The reply code received from the server, - * may be {@code null} in which case the command is sent with no parameters - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - * @since 3.0 - */ - public int mlst(String path) throws IOException { - return sendCommand(FTPCmd.MLST, path); - } - - /*** - * A convenience method to send the FTP NLST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int nlst() throws IOException { - return sendCommand(FTPCmd.NLST); - } - - /*** - * A convenience method to send the FTP NLST command to the server, - * receive the reply, and return the reply code. Remember, it is up - * to you to manage the data connection. If you don't need this low - * level of access, use {@link FTPClient} - * , which will handle all low level details for you. - * - * @param pathname The pathname to list, - * may be {@code null} in which case the command is sent with no parameters - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int nlst(String pathname) throws IOException { - return sendCommand(FTPCmd.NLST, pathname); - } - - /*** - * A convenience method to send the FTP SITE command to the server, - * receive the reply, and return the reply code. - * - * @param parameters The site parameters to send. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int site(String parameters) throws IOException { - return sendCommand(FTPCmd.SITE, parameters); - } - - /*** - * A convenience method to send the FTP SYST command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int syst() throws IOException { - return sendCommand(FTPCmd.SYST); - } - - /*** - * A convenience method to send the FTP STAT command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int stat() throws IOException { - return sendCommand(FTPCmd.STAT); - } - - /*** - * A convenience method to send the FTP STAT command to the server, - * receive the reply, and return the reply code. - * - * @param pathname A pathname to list. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int stat(String pathname) throws IOException { - return sendCommand(FTPCmd.STAT, pathname); - } - - /*** - * A convenience method to send the FTP HELP command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int help() throws IOException { - return sendCommand(FTPCmd.HELP); - } - - /*** - * A convenience method to send the FTP HELP command to the server, - * receive the reply, and return the reply code. - * - * @param command The command name on which to request help. - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int help(String command) throws IOException { - return sendCommand(FTPCmd.HELP, command); - } - - /*** - * A convenience method to send the FTP NOOP command to the server, - * receive the reply, and return the reply code. - * - * @return The reply code received from the server. - * @throws FTPConnectionClosedException - * If the FTP server prematurely closes the connection as a result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending the - * command or receiving the server reply. - ***/ - public int noop() throws IOException { - return sendCommand(FTPCmd.NOOP); - } - - /** - * Return whether strict multiline parsing is enabled, as per RFC 959, section 4.2. - * - * @return True if strict, false if lenient - * @since 2.0 - */ - public boolean isStrictMultilineParsing() { - return strictMultilineParsing; - } - - /** - * Set strict multiline parsing. - * - * @param strictMultilineParsing the setting - * @since 2.0 - */ - public void setStrictMultilineParsing(boolean strictMultilineParsing) { - this.strictMultilineParsing = strictMultilineParsing; - } - - /** - * Return whether strict non-multiline parsing is enabled, as per RFC 959, section 4.2. - *

- * The default is true, which requires the 3 digit code be followed by space and some text. - *
- * If false, only the 3 digit code is required (as was the case for versions up to 3.5) - *
- * - * @return True if strict (default), false if additional checks are not made - * @since 3.6 - */ - public boolean isStrictReplyParsing() { - return strictReplyParsing; - } - - /** - * Set strict non-multiline parsing. - *

- * If true, it requires the 3 digit code be followed by space and some text. - *
- * If false, only the 3 digit code is required (as was the case for versions up to 3.5) - *

- * This should not be required by a well-behaved FTP server - *
- * - * @param strictReplyParsing the setting - * @since 3.6 - */ - public void setStrictReplyParsing(boolean strictReplyParsing) { - this.strictReplyParsing = strictReplyParsing; - } - - /** - * Provide command support to super-class - */ - @Override protected ProtocolCommandSupport getCommandSupport() { - return _commandSupport_; - } -} - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClient.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClient.java deleted file mode 100644 index 4bcf98dc..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClient.java +++ /dev/null @@ -1,3787 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package aria.apache.commons.net.ftp; - -import aria.apache.commons.net.SocketClient; -import aria.apache.commons.net.ftp.parser.ParserInitializationException; -import aria.apache.commons.net.io.CopyStreamAdapter; -import aria.apache.commons.net.io.CopyStreamEvent; -import aria.apache.commons.net.io.CopyStreamException; -import aria.apache.commons.net.io.CopyStreamListener; -import aria.apache.commons.net.io.FromNetASCIIInputStream; -import aria.apache.commons.net.io.SocketInputStream; -import aria.apache.commons.net.io.SocketOutputStream; -import aria.apache.commons.net.io.ToNetASCIIOutputStream; -import aria.apache.commons.net.io.Util; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Properties; -import java.util.Random; -import java.util.Set; - -import aria.apache.commons.net.MalformedServerReplyException; -import aria.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory; -import aria.apache.commons.net.ftp.parser.FTPFileEntryParserFactory; -import aria.apache.commons.net.ftp.parser.MLSxEntryParser; -import aria.apache.commons.net.io.CRLFLineReader; - -/** - * FTPClient encapsulates all the functionality necessary to store and - * retrieve files from an FTP server. This class takes care of all - * low level details of interacting with an FTP server and provides - * a convenient higher level interface. As with all classes derived - * from {@link SocketClient}, - * you must first connect to the server with - * {@link SocketClient#connect connect } - * before doing anything, and finally - * {@link SocketClient#disconnect disconnect } - * after you're completely finished interacting with the server. - * Then you need to check the FTP reply code to see if the connection - * was successful. For example: - *

- *    FTPClient ftp = new FTPClient();
- *    FTPClientConfig config = new FTPClientConfig();
- *    config.setXXX(YYY); // change required options
- *    // for example config.setServerTimeZoneId("Pacific/Pitcairn")
- *    ftp.configure(config );
- *    boolean error = false;
- *    try {
- *      int reply;
- *      String server = "ftp.example.com";
- *      ftp.connect(server);
- *      System.out.println("Connected to " + server + ".");
- *      System.out.print(ftp.getReplyString());
- *
- *      // After connection attempt, you should check the reply code to verify
- *      // success.
- *      reply = ftp.getReplyCode();
- *
- *      if(!FTPReply.isPositiveCompletion(reply)) {
- *        ftp.disconnect();
- *        System.err.println("FTP server refused connection.");
- *        System.exit(1);
- *      }
- *      ... // transfer files
- *      ftp.logout();
- *    } catch(IOException e) {
- *      error = true;
- *      e.printStackTrace();
- *    } finally {
- *      if(ftp.isConnected()) {
- *        try {
- *          ftp.disconnect();
- *        } catch(IOException ioe) {
- *          // do nothing
- *        }
- *      }
- *      System.exit(error ? 1 : 0);
- *    }
- * 
- *

- * Immediately after connecting is the only real time you need to check the - * reply code (because connect is of type void). The convention for all the - * FTP command methods in FTPClient is such that they either return a - * boolean value or some other value. - * The boolean methods return true on a successful completion reply from - * the FTP server and false on a reply resulting in an error condition or - * failure. The methods returning a value other than boolean return a value - * containing the higher level data produced by the FTP command, or null if a - * reply resulted in an error condition or failure. If you want to access - * the exact FTP reply code causing a success or failure, you must call - * {@link FTP#getReplyCode getReplyCode } after - * a success or failure. - *

- * The default settings for FTPClient are for it to use - * FTP.ASCII_FILE_TYPE , - * FTP.NON_PRINT_TEXT_FORMAT , - * FTP.STREAM_TRANSFER_MODE , and - * FTP.FILE_STRUCTURE . The only file types directly supported - * are FTP.ASCII_FILE_TYPE and - * FTP.BINARY_FILE_TYPE . Because there are at least 4 - * different EBCDIC encodings, we have opted not to provide direct support - * for EBCDIC. To transfer EBCDIC and other unsupported file types you - * must create your own filter InputStreams and OutputStreams and wrap - * them around the streams returned or required by the FTPClient methods. - * FTPClient uses the {@link ToNetASCIIOutputStream NetASCII} - * filter streams to provide transparent handling of ASCII files. We will - * consider incorporating EBCDIC support if there is enough demand. - *

- * FTP.NON_PRINT_TEXT_FORMAT , - * FTP.STREAM_TRANSFER_MODE , and - * FTP.FILE_STRUCTURE are the only supported formats, - * transfer modes, and file structures. - *

- * Because the handling of sockets on different platforms can differ - * significantly, the FTPClient automatically issues a new PORT (or EPRT) command - * prior to every transfer requiring that the server connect to the client's - * data port. This ensures identical problem-free behavior on Windows, Unix, - * and Macintosh platforms. Additionally, it relieves programmers from - * having to issue the PORT (or EPRT) command themselves and dealing with platform - * dependent issues. - *

- * Additionally, for security purposes, all data connections to the - * client are verified to ensure that they originated from the intended - * party (host and port). If a data connection is initiated by an unexpected - * party, the command will close the socket and throw an IOException. You - * may disable this behavior with - * {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}. - *

- * You should keep in mind that the FTP server may choose to prematurely - * close a connection if the client has been idle for longer than a - * given time period (usually 900 seconds). The FTPClient class will detect a - * premature FTP server connection closing when it receives a - * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } - * response to a command. - * When that occurs, the FTP class method encountering that reply will throw - * an {@link FTPConnectionClosedException} - * . - * FTPConnectionClosedException - * is a subclass of IOException and therefore need not be - * caught separately, but if you are going to catch it separately, its - * catch block must appear before the more general IOException - * catch block. When you encounter an - * {@link FTPConnectionClosedException} - * , you must disconnect the connection with - * {@link #disconnect disconnect() } to properly clean up the - * system resources used by FTPClient. Before disconnecting, you may check the - * last reply code and text with - * {@link FTP#getReplyCode getReplyCode }, - * {@link FTP#getReplyString getReplyString }, - * and - * {@link FTP#getReplyStrings getReplyStrings}. - * You may avoid server disconnections while the client is idle by - * periodically sending NOOP commands to the server. - *

- * Rather than list it separately for each method, we mention here that - * every method communicating with the server and throwing an IOException - * can also throw a - * {@link MalformedServerReplyException} - * , which is a subclass - * of IOException. A MalformedServerReplyException will be thrown when - * the reply received from the server deviates enough from the protocol - * specification that it cannot be interpreted in a useful manner despite - * attempts to be as lenient as possible. - *

- * Listing API Examples - * Both paged and unpaged examples of directory listings are available, - * as follows: - *

- * Unpaged (whole list) access, using a parser accessible by auto-detect: - *

- *    FTPClient f = new FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = f.listFiles(directory);
- * 
- *

- * Paged access, using a parser not accessible by auto-detect. The class - * defined in the first parameter of initateListParsing should be derived - * from org.apache.commons.net.FTPFileEntryParser: - *

- *    FTPClient f = new FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPListParseEngine engine =
- *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
- *
- *    while (engine.hasNext()) {
- *       FTPFile[] files = engine.getNext(25);  // "page size" you want
- *       //do whatever you want with these files, display them, etc.
- *       //expensive FTPFile objects not created until needed.
- *    }
- * 
- *

- * Paged access, using a parser accessible by auto-detect: - *

- *    FTPClient f = new FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPListParseEngine engine = f.initiateListParsing(directory);
- *
- *    while (engine.hasNext()) {
- *       FTPFile[] files = engine.getNext(25);  // "page size" you want
- *       //do whatever you want with these files, display them, etc.
- *       //expensive FTPFile objects not created until needed.
- *    }
- * 
- *

- * For examples of using FTPClient on servers whose directory listings - *

    - *
  • use languages other than English
  • - *
  • use date formats other than the American English "standard" MM d yyyy
  • - *
  • are in different timezones and you need accurate timestamps for dependency checking - * as in Ant
  • - *
see {@link FTPClientConfig FTPClientConfig}. - *

- * Control channel keep-alive feature: - *

- * Please note: this does not apply to the methods where the user is responsible for writing - * or reading - * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} - * and the other xxxFileStream methods - *

- * During file transfers, the data connection is busy, but the control connection is idle. - * FTP servers know that the control connection is in use, so won't close it through lack of - * activity, - * but it's a lot harder for network routers to know that the control and data connections are - * associated - * with each other. - * Some routers may treat the control connection as idle, and disconnect it if the transfer over the - * data - * connection takes longer than the allowable idle time for the router. - *
- * One solution to this is to send a safe command (i.e. NOOP) over the control connection to reset - * the router's - * idle timer. This is enabled as follows: - *

- *     ftpClient.setControlKeepAliveTimeout(300); // set timeout to 5 minutes
- * 
- * This will cause the file upload/download methods to send a NOOP approximately every 5 minutes. - * The following public methods support this: - *
    - *
  • {@link #retrieveFile(String, OutputStream)}
  • - *
  • {@link #appendFile(String, InputStream)}
  • - *
  • {@link #storeFile(String, InputStream)}
  • - *
  • {@link #storeUniqueFile(InputStream)}
  • - *
  • {@link #storeUniqueFileStream(String)}
  • - *
- * This feature does not apply to the methods where the user is responsible for writing or reading - * the data stream, i.e. {@link #retrieveFileStream(String)} , {@link #storeFileStream(String)} - * and the other xxxFileStream methods. - * In such cases, the user is responsible for keeping the control connection alive if necessary. - *

- * The implementation currently uses a {@link CopyStreamListener} which is passed to the - * {@link Util#copyStream(InputStream, OutputStream, int, long, CopyStreamListener, boolean)} - * method, so the timing is partially dependent on how long each block transfer takes. - * - * @see #FTP_SYSTEM_TYPE - * @see #SYSTEM_TYPE_PROPERTIES - * @see FTP - * @see FTPConnectionClosedException - * @see FTPFileEntryParser - * @see FTPFileEntryParserFactory - * @see DefaultFTPFileEntryParserFactory - * @see FTPClientConfig - * @see MalformedServerReplyException - */ -public class FTPClient extends FTP implements Configurable { - /** - * The system property ({@value}) which can be used to override the system type.
- * If defined, the value will be used to create any automatically created parsers. - * - * @since 3.0 - */ - public static final String FTP_SYSTEM_TYPE = "org.apache.commons.net.ftp.systemType"; - - /** - * The system property ({@value}) which can be used as the default system type.
- * If defined, the value will be used if the SYST command fails. - * - * @since 3.1 - */ - public static final String FTP_SYSTEM_TYPE_DEFAULT = - "org.apache.commons.net.ftp.systemType.default"; - - /** - * The name of an optional systemType properties file ({@value}), which is loaded - * using {@link Class#getResourceAsStream(String)}.
- * The entries are the systemType (as determined by {@link FTPClient#getSystemType}) - * and the values are the replacement type or parserClass, which is passed to - * {@link FTPFileEntryParserFactory#createFileEntryParser(String)}.
- * For example: - *

-   * Plan 9=Unix
-   * OS410=OS400FTPEntryParser
-   * 
- * - * @since 3.0 - */ - public static final String SYSTEM_TYPE_PROPERTIES = "/systemType.properties"; - - /** - * A constant indicating the FTP session is expecting all transfers - * to occur between the client (local) and server and that the server - * should connect to the client's data port to initiate a data transfer. - * This is the default data connection mode when and FTPClient instance - * is created. - */ - public static final int ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0; - /** - * A constant indicating the FTP session is expecting all transfers - * to occur between two remote servers and that the server - * the client is connected to should connect to the other server's - * data port to initiate a data transfer. - */ - public static final int ACTIVE_REMOTE_DATA_CONNECTION_MODE = 1; - /** - * A constant indicating the FTP session is expecting all transfers - * to occur between the client (local) and server and that the server - * is in passive mode, requiring the client to connect to the - * server's data port to initiate a transfer. - */ - public static final int PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2; - /** - * A constant indicating the FTP session is expecting all transfers - * to occur between two remote servers and that the server - * the client is connected to is in passive mode, requiring the other - * server to connect to the first server's data port to initiate a data - * transfer. - */ - public static final int PASSIVE_REMOTE_DATA_CONNECTION_MODE = 3; - - private int __dataConnectionMode; - private int __dataTimeout; - private int __passivePort; - private String __passiveHost; - private final Random __random; - private int __activeMinPort; - private int __activeMaxPort; - private InetAddress __activeExternalHost; - private InetAddress __reportActiveExternalHost; - // overrides __activeExternalHost in EPRT/PORT commands - /** The address to bind to on passive connections, if necessary. */ - private InetAddress __passiveLocalHost; - - private int __fileType; - @SuppressWarnings("unused") // fields are written, but currently not read - private int __fileFormat; - @SuppressWarnings("unused") // field is written, but currently not read - private int __fileStructure; - @SuppressWarnings("unused") // field is written, but currently not read - private int __fileTransferMode; - private boolean __remoteVerificationEnabled; - private long __restartOffset; - private FTPFileEntryParserFactory __parserFactory; - private int __bufferSize; // buffersize for buffered data streams - private int __sendDataSocketBufferSize; - private int __receiveDataSocketBufferSize; - private boolean __listHiddenFiles; - private boolean __useEPSVwithIPv4; // whether to attempt EPSV with an IPv4 connection - - // __systemName is a cached value that should not be referenced directly - // except when assigned in getSystemName and __initDefaults. - private String __systemName; - - // __entryParser is a cached value that should not be referenced directly - // except when assigned in listFiles(String, String) and __initDefaults. - private FTPFileEntryParser __entryParser; - - // Key used to create the parser; necessary to ensure that the parser type is not ignored - private String __entryParserKey; - - private FTPClientConfig __configuration; - - // Listener used by store/retrieve methods to handle keepalive - private CopyStreamListener __copyStreamListener; - - // How long to wait before sending another control keep-alive message - private long __controlKeepAliveTimeout; - - // How long to wait (ms) for keepalive message replies before continuing - // Most FTP servers don't seem to support concurrent control and data connection usage - private int __controlKeepAliveReplyTimeout = 1000; - - /** - * Enable or disable replacement of internal IP in passive mode. Default enabled - * using {code NatServerResolverImpl}. - */ - private HostnameResolver __passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); - - /** Pattern for PASV mode responses. Groups: (n,n,n,n),(n),(n) */ - private static final java.util.regex.Pattern __PARMS_PAT; - - static { - __PARMS_PAT = java.util.regex.Pattern.compile( - "(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})"); - } - - /** Controls the automatic server encoding detection (only UTF-8 supported). */ - private boolean __autodetectEncoding = false; - - /** Map of FEAT responses. If null, has not been initialised. */ - private HashMap> __featuresMap; - - private static class PropertiesSingleton { - - static final Properties PROPERTIES; - - static { - InputStream resourceAsStream = FTPClient.class.getResourceAsStream(SYSTEM_TYPE_PROPERTIES); - Properties p = null; - if (resourceAsStream != null) { - p = new Properties(); - try { - p.load(resourceAsStream); - } catch (IOException e) { - // Ignored - } finally { - try { - resourceAsStream.close(); - } catch (IOException e) { - // Ignored - } - } - } - PROPERTIES = p; - } - } - - private static Properties getOverrideProperties() { - return PropertiesSingleton.PROPERTIES; - } - - /** - * Default FTPClient constructor. Creates a new FTPClient instance - * with the data connection mode set to - * ACTIVE_LOCAL_DATA_CONNECTION_MODE , the file type - * set to FTP.ASCII_FILE_TYPE , the - * file format set to FTP.NON_PRINT_TEXT_FORMAT , - * the file structure set to FTP.FILE_STRUCTURE , and - * the transfer mode set to FTP.STREAM_TRANSFER_MODE . - *

- * The list parsing auto-detect feature can be configured to use lenient future - * dates (short dates may be up to one day in the future) as follows: - *

-   * FTPClient ftp = new FTPClient();
-   * FTPClientConfig config = new FTPClientConfig();
-   * config.setLenientFutureDates(true);
-   * ftp.configure(config );
-   * 
- */ - public FTPClient() { - __initDefaults(); - __dataTimeout = -1; - __remoteVerificationEnabled = true; - __parserFactory = new DefaultFTPFileEntryParserFactory(); - __configuration = null; - __listHiddenFiles = false; - __useEPSVwithIPv4 = false; - __random = new Random(); - __passiveLocalHost = null; - } - - private void __initDefaults() { - __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; - __passiveHost = null; - __passivePort = -1; - __activeExternalHost = null; - __reportActiveExternalHost = null; - __activeMinPort = 0; - __activeMaxPort = 0; - __fileType = FTP.ASCII_FILE_TYPE; - __fileStructure = FTP.FILE_STRUCTURE; - __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; - __fileTransferMode = FTP.STREAM_TRANSFER_MODE; - __restartOffset = 0; - __systemName = null; - __entryParser = null; - __entryParserKey = ""; - __featuresMap = null; - } - - /** - * Parse the pathname from a CWD reply. - *

- * According to RFC959 (http://www.ietf.org/rfc/rfc959.txt), - * it should be the same as for MKD i.e. - * {@code 257""[commentary]} - * where any double-quotes in {@code } are doubled. - * Unlike MKD, the commentary is optional. - *

- * However, see NET-442 for an exception. - * - * @return the pathname, without enclosing quotes, - * or the full string after the reply code and space if the syntax is invalid - * (i.e. enclosing quotes are missing or embedded quotes are not doubled) - */ - // package protected for access by test cases - static String __parsePathname(String reply) { - String param = reply.substring(REPLY_CODE_LEN + 1); - if (param.startsWith("\"")) { - StringBuilder sb = new StringBuilder(); - boolean quoteSeen = false; - // start after initial quote - for (int i = 1; i < param.length(); i++) { - char ch = param.charAt(i); - if (ch == '"') { - if (quoteSeen) { - sb.append(ch); - quoteSeen = false; - } else { - // don't output yet, in case doubled - quoteSeen = true; - } - } else { - if (quoteSeen) { // found lone trailing quote within string - return sb.toString(); - } - sb.append(ch); // just another character - } - } - if (quoteSeen) { // found lone trailing quote at end of string - return sb.toString(); - } - } - // malformed reply, return all after reply code and space - return param; - } - - /** - * @param reply the reply to parse - * @throws MalformedServerReplyException if the server reply does not match (n,n,n,n),(n),(n) - * @since 3.1 - */ - protected void _parsePassiveModeReply(String reply) throws MalformedServerReplyException { - java.util.regex.Matcher m = __PARMS_PAT.matcher(reply); - if (!m.find()) { - throw new MalformedServerReplyException( - "Could not parse passive host information.\nServer Reply: " + reply); - } - - __passiveHost = m.group(1).replace(',', '.'); // Fix up to look like IP address - - try { - int oct1 = Integer.parseInt(m.group(2)); - int oct2 = Integer.parseInt(m.group(3)); - __passivePort = (oct1 << 8) | oct2; - } catch (NumberFormatException e) { - throw new MalformedServerReplyException( - "Could not parse passive port information.\nServer Reply: " + reply); - } - - if (__passiveNatWorkaroundStrategy != null) { - try { - String passiveHost = __passiveNatWorkaroundStrategy.resolve(__passiveHost); - if (!__passiveHost.equals(passiveHost)) { - fireReplyReceived(0, "[Replacing PASV mode reply address " - + __passiveHost - + " with " - + passiveHost - + "]\n"); - __passiveHost = passiveHost; - } - } catch (UnknownHostException e) { // Should not happen as we are passing in an IP address - throw new MalformedServerReplyException( - "Could not parse passive host information.\nServer Reply: " + reply); - } - } - } - - protected void _parseExtendedPassiveModeReply(String reply) throws MalformedServerReplyException { - reply = reply.substring(reply.indexOf('(') + 1, reply.indexOf(')')).trim(); - - char delim1, delim2, delim3, delim4; - delim1 = reply.charAt(0); - delim2 = reply.charAt(1); - delim3 = reply.charAt(2); - delim4 = reply.charAt(reply.length() - 1); - - if (!(delim1 == delim2) || !(delim2 == delim3) || !(delim3 == delim4)) { - throw new MalformedServerReplyException( - "Could not parse extended passive host information.\nServer Reply: " + reply); - } - - int port; - try { - port = Integer.parseInt(reply.substring(3, reply.length() - 1)); - } catch (NumberFormatException e) { - throw new MalformedServerReplyException( - "Could not parse extended passive host information.\nServer Reply: " + reply); - } - - // in EPSV mode, the passive host address is implicit - __passiveHost = getRemoteAddress().getHostAddress(); - __passivePort = port; - } - - private boolean __storeFile(FTPCmd command, String remote, InputStream local) throws IOException { - return _storeFile(command.getCommand(), remote, local); - } - - /** - * @param command the command to send - * @param remote the remote file name - * @param local the local file name - * @return true if successful - * @throws IOException on error - * @since 3.1 - */ - protected boolean _storeFile(String command, String remote, InputStream local) - throws IOException { - Socket socket = _openDataConnection_(command, remote); - - if (socket == null) { - return false; - } - - final OutputStream output; - - if (__fileType == ASCII_FILE_TYPE) { - output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); - } else { - output = getBufferedOutputStream(socket.getOutputStream()); - } - - CSL csl = null; - if (__controlKeepAliveTimeout > 0) { - if (mListener != null) { - csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout, mListener); - } else { - csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); - } - } - - // Treat everything else as binary for now - try { - Util.copyStream(local, output, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, - __mergeListeners(csl), false); - } catch (IOException e) { - Util.closeQuietly(socket); // ignore close errors here - if (csl != null) { - csl.cleanUp(); // fetch any outstanding keepalive replies - } - throw e; - } - - output.close(); // ensure the file is fully written - socket.close(); // done writing the file - if (csl != null) { - csl.cleanUp(); // fetch any outstanding keepalive replies - } - // Get the transfer response - boolean ok = completePendingCommand(); - return ok; - } - - private OutputStream __storeFileStream(FTPCmd command, String remote) throws IOException { - return _storeFileStream(command.getCommand(), remote); - } - - /** - * @param command the command to send - * @param remote the remote file name - * @return the output stream to write to - * @throws IOException on error - * @since 3.1 - */ - protected OutputStream _storeFileStream(String command, String remote) throws IOException { - Socket socket = _openDataConnection_(command, remote); - - if (socket == null) { - return null; - } - - final OutputStream output; - if (__fileType == ASCII_FILE_TYPE) { - // We buffer ascii transfers because the buffering has to - // be interposed between ToNetASCIIOutputSream and the underlying - // socket output stream. We don't buffer binary transfers - // because we don't want to impose a buffering policy on the - // programmer if possible. Programmers can decide on their - // own if they want to wrap the SocketOutputStream we return - // for file types other than ASCII. - output = new ToNetASCIIOutputStream(getBufferedOutputStream(socket.getOutputStream())); - } else { - output = socket.getOutputStream(); - } - return new SocketOutputStream(socket, output); - } - - /** - * Establishes a data connection with the FTP server, returning - * a Socket for the connection if successful. If a restart - * offset has been set with {@link #setRestartOffset(long)}, - * a REST command is issued to the server with the offset as - * an argument before establishing the data connection. Active - * mode connections also cause a local PORT command to be issued. - * - * @param command The int representation of the FTP command to send. - * @param arg The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return A Socket corresponding to the established data connection. - * Null is returned if an FTP protocol error is reported at - * any point during the establishment and initialization of - * the connection. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @deprecated (3.3) Use {@link #_openDataConnection_(FTPCmd, String)} instead - */ - @Deprecated protected Socket _openDataConnection_(int command, String arg) throws IOException { - return _openDataConnection_(FTPCommand.getCommand(command), arg); - } - - /** - * Establishes a data connection with the FTP server, returning - * a Socket for the connection if successful. If a restart - * offset has been set with {@link #setRestartOffset(long)}, - * a REST command is issued to the server with the offset as - * an argument before establishing the data connection. Active - * mode connections also cause a local PORT command to be issued. - * - * @param command The int representation of the FTP command to send. - * @param arg The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return A Socket corresponding to the established data connection. - * Null is returned if an FTP protocol error is reported at - * any point during the establishment and initialization of - * the connection. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.3 - */ - protected Socket _openDataConnection_(FTPCmd command, String arg) throws IOException { - return _openDataConnection_(command.getCommand(), arg); - } - - /** - * Establishes a data connection with the FTP server, returning - * a Socket for the connection if successful. If a restart - * offset has been set with {@link #setRestartOffset(long)}, - * a REST command is issued to the server with the offset as - * an argument before establishing the data connection. Active - * mode connections also cause a local PORT command to be issued. - * - * @param command The text representation of the FTP command to send. - * @param arg The arguments to the FTP command. If this parameter is - * set to null, then the command is sent with no argument. - * @return A Socket corresponding to the established data connection. - * Null is returned if an FTP protocol error is reported at - * any point during the establishment and initialization of - * the connection. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.1 - */ - protected Socket _openDataConnection_(String command, String arg) throws IOException { - if (__dataConnectionMode != ACTIVE_LOCAL_DATA_CONNECTION_MODE - && __dataConnectionMode != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { - return null; - } - - final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; - - Socket socket; - - if (__dataConnectionMode == ACTIVE_LOCAL_DATA_CONNECTION_MODE) { - // if no activePortRange was set (correctly) -> getActivePort() = 0 - // -> new ServerSocket(0) -> bind to any free local port - ServerSocket server = - _serverSocketFactory_.createServerSocket(getActivePort(), 1, getHostAddress()); - - try { - // Try EPRT only if remote server is over IPv6, if not use PORT, - // because EPRT has no advantage over PORT on IPv4. - // It could even have the disadvantage, - // that EPRT will make the data connection fail, because - // today's intelligent NAT Firewalls are able to - // substitute IP addresses in the PORT command, - // but might not be able to recognize the EPRT command. - if (isInet6Address) { - if (!FTPReply.isPositiveCompletion(eprt(getReportHostAddress(), server.getLocalPort()))) { - return null; - } - } else { - if (!FTPReply.isPositiveCompletion(port(getReportHostAddress(), server.getLocalPort()))) { - return null; - } - } - - if ((__restartOffset > 0) && !restart(__restartOffset)) { - return null; - } - - if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { - return null; - } - - // For now, let's just use the data timeout value for waiting for - // the data connection. It may be desirable to let this be a - // separately configurable value. In any case, we really want - // to allow preventing the accept from blocking indefinitely. - if (__dataTimeout >= 0) { - server.setSoTimeout(__dataTimeout); - } - socket = server.accept(); - - // Ensure the timeout is set before any commands are issued on the new socket - if (__dataTimeout >= 0) { - socket.setSoTimeout(__dataTimeout); - } - if (__receiveDataSocketBufferSize > 0) { - socket.setReceiveBufferSize(__receiveDataSocketBufferSize); - } - if (__sendDataSocketBufferSize > 0) { - socket.setSendBufferSize(__sendDataSocketBufferSize); - } - } finally { - server.close(); - } - } else { // We must be in PASSIVE_LOCAL_DATA_CONNECTION_MODE - - // Try EPSV command first on IPv6 - and IPv4 if enabled. - // When using IPv4 with NAT it has the advantage - // to work with more rare configurations. - // E.g. if FTP server has a static PASV address (external network) - // and the client is coming from another internal network. - // In that case the data connection after PASV command would fail, - // while EPSV would make the client succeed by taking just the port. - boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; - if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { - _parseExtendedPassiveModeReply(_replyLines.get(0)); - } else { - if (isInet6Address) { - return null; // Must use EPSV for IPV6 - } - // If EPSV failed on IPV4, revert to PASV - if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { - return null; - } - _parsePassiveModeReply(_replyLines.get(0)); - } - - socket = _socketFactory_.createSocket(); - if (__receiveDataSocketBufferSize > 0) { - socket.setReceiveBufferSize(__receiveDataSocketBufferSize); - } - if (__sendDataSocketBufferSize > 0) { - socket.setSendBufferSize(__sendDataSocketBufferSize); - } - if (__passiveLocalHost != null) { - socket.bind(new InetSocketAddress(__passiveLocalHost, 0)); - } - - // For now, let's just use the data timeout value for waiting for - // the data connection. It may be desirable to let this be a - // separately configurable value. In any case, we really want - // to allow preventing the accept from blocking indefinitely. - if (__dataTimeout >= 0) { - socket.setSoTimeout(__dataTimeout); - } - - socket.connect(new InetSocketAddress(__passiveHost, __passivePort), connectTimeout); - if ((__restartOffset > 0) && !restart(__restartOffset)) { - socket.close(); - return null; - } - - if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { - socket.close(); - return null; - } - } - - if (__remoteVerificationEnabled && !verifyRemote(socket)) { - socket.close(); - - throw new IOException("Host attempting data connection " - + socket.getInetAddress().getHostAddress() - + " is not same as server " - + getRemoteAddress().getHostAddress()); - } - - return socket; - } - - @Override protected void _connectAction_() throws IOException { - _connectAction_(null); - } - - /** - * @param socketIsReader the reader to reuse (if non-null) - * @throws IOException on error - * @since 3.4 - */ - @Override protected void _connectAction_(Reader socketIsReader) throws IOException { - super._connectAction_(socketIsReader); // sets up _input_ and _output_ - __initDefaults(); - // must be after super._connectAction_(), because otherwise we get an - // Exception claiming we're not connected - if (__autodetectEncoding) { - ArrayList oldReplyLines = new ArrayList(_replyLines); - int oldReplyCode = _replyCode; - if (hasFeature("UTF8") || hasFeature("UTF-8")) // UTF8 appears to be the default - { - setControlEncoding("UTF-8"); - _controlInput_ = new CRLFLineReader(new InputStreamReader(_input_, getControlEncoding())); - _controlOutput_ = - new BufferedWriter(new OutputStreamWriter(_output_, getControlEncoding())); - } - // restore the original reply (server greeting) - _replyLines.clear(); - _replyLines.addAll(oldReplyLines); - _replyCode = oldReplyCode; - _newReplyString = true; - } - } - - /** - * Sets the timeout in milliseconds to use when reading from the - * data connection. This timeout will be set immediately after - * opening the data connection, provided that the value is ≥ 0. - *

- * Note: the timeout will also be applied when calling accept() - * whilst establishing an active local data connection. - * - * @param timeout The default timeout in milliseconds that is used when - * opening a data connection socket. The value 0 means an infinite timeout. - */ - public void setDataTimeout(int timeout) { - __dataTimeout = timeout; - } - - /** - * set the factory used for parser creation to the supplied factory object. - * - * @param parserFactory factory object used to create FTPFileEntryParsers - * @see FTPFileEntryParserFactory - * @see DefaultFTPFileEntryParserFactory - */ - public void setParserFactory(FTPFileEntryParserFactory parserFactory) { - __parserFactory = parserFactory; - } - - /** - * Closes the connection to the FTP server and restores - * connection parameters to the default values. - * - * @throws IOException If an error occurs while disconnecting. - */ - @Override public void disconnect() throws IOException { - super.disconnect(); - __initDefaults(); - } - - /** - * Enable or disable verification that the remote host taking part - * of a data connection is the same as the host to which the control - * connection is attached. The default is for verification to be - * enabled. You may set this value at any time, whether the - * FTPClient is currently connected or not. - * - * @param enable True to enable verification, false to disable verification. - */ - public void setRemoteVerificationEnabled(boolean enable) { - __remoteVerificationEnabled = enable; - } - - /** - * Return whether or not verification of the remote host participating - * in data connections is enabled. The default behavior is for - * verification to be enabled. - * - * @return True if verification is enabled, false if not. - */ - public boolean isRemoteVerificationEnabled() { - return __remoteVerificationEnabled; - } - - /** - * Login to the FTP server using the provided username and password. - * - * @param username The username to login under. - * @param password The password to use. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean login(String username, String password) throws IOException { - - user(username); - - if (FTPReply.isPositiveCompletion(_replyCode)) { - return true; - } - - // If we get here, we either have an error code, or an intermmediate - // reply requesting password. - if (!FTPReply.isPositiveIntermediate(_replyCode)) { - return false; - } - - return FTPReply.isPositiveCompletion(pass(password)); - } - - /** - * Login to the FTP server using the provided username, password, - * and account. If no account is required by the server, only - * the username and password, the account information is not used. - * - * @param username The username to login under. - * @param password The password to use. - * @param account The account to use. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean login(String username, String password, String account) throws IOException { - user(username); - - if (FTPReply.isPositiveCompletion(_replyCode)) { - return true; - } - - // If we get here, we either have an error code, or an intermmediate - // reply requesting password. - if (!FTPReply.isPositiveIntermediate(_replyCode)) { - return false; - } - - pass(password); - - if (FTPReply.isPositiveCompletion(_replyCode)) { - return true; - } - - if (!FTPReply.isPositiveIntermediate(_replyCode)) { - return false; - } - - return FTPReply.isPositiveCompletion(acct(account)); - } - - /** - * Logout of the FTP server by sending the QUIT command. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean logout() throws IOException { - return FTPReply.isPositiveCompletion(quit()); - } - - /** - * Change the current working directory of the FTP session. - * - * @param pathname The new current working directory. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean changeWorkingDirectory(String pathname) throws IOException { - return FTPReply.isPositiveCompletion(cwd(pathname)); - } - - /** - * Change to the parent directory of the current working directory. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean changeToParentDirectory() throws IOException { - return FTPReply.isPositiveCompletion(cdup()); - } - - /** - * Issue the FTP SMNT command. - * - * @param pathname The pathname to mount. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean structureMount(String pathname) throws IOException { - return FTPReply.isPositiveCompletion(smnt(pathname)); - } - - /** - * Reinitialize the FTP session. Not all FTP servers support this - * command, which issues the FTP REIN command. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.4 (made public) - */ - public boolean reinitialize() throws IOException { - rein(); - - if (FTPReply.isPositiveCompletion(_replyCode) || (FTPReply.isPositivePreliminary(_replyCode) - && FTPReply.isPositiveCompletion(getReply()))) { - - __initDefaults(); - - return true; - } - - return false; - } - - /** - * Set the current data connection mode to - * ACTIVE_LOCAL_DATA_CONNECTION_MODE. No communication - * with the FTP server is conducted, but this causes all future data - * transfers to require the FTP server to connect to the client's - * data port. Additionally, to accommodate differences between socket - * implementations on different platforms, this method causes the - * client to issue a PORT command before every data transfer. - */ - public void enterLocalActiveMode() { - __dataConnectionMode = ACTIVE_LOCAL_DATA_CONNECTION_MODE; - __passiveHost = null; - __passivePort = -1; - } - - /** - * Set the current data connection mode to - * PASSIVE_LOCAL_DATA_CONNECTION_MODE . Use this - * method only for data transfers between the client and server. - * This method causes a PASV (or EPSV) command to be issued to the server - * before the opening of every data connection, telling the server to - * open a data port to which the client will connect to conduct - * data transfers. The FTPClient will stay in - * PASSIVE_LOCAL_DATA_CONNECTION_MODE until the - * mode is changed by calling some other method such as - * {@link #enterLocalActiveMode enterLocalActiveMode() } - *

- * N.B. currently calling any connect method will reset the mode to - * ACTIVE_LOCAL_DATA_CONNECTION_MODE. - */ - public void enterLocalPassiveMode() { - __dataConnectionMode = PASSIVE_LOCAL_DATA_CONNECTION_MODE; - // These will be set when just before a data connection is opened - // in _openDataConnection_() - __passiveHost = null; - __passivePort = -1; - } - - /** - * Set the current data connection mode to - * ACTIVE_REMOTE_DATA_CONNECTION . Use this method only - * for server to server data transfers. This method issues a PORT - * command to the server, indicating the other server and port to which - * it should connect for data transfers. You must call this method - * before EVERY server to server transfer attempt. The FTPClient will - * NOT automatically continue to issue PORT commands. You also - * must remember to call - * {@link #enterLocalActiveMode enterLocalActiveMode() } if you - * wish to return to the normal data connection mode. - * - * @param host The passive mode server accepting connections for data - * transfers. - * @param port The passive mode server's data port. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean enterRemoteActiveMode(InetAddress host, int port) throws IOException { - if (FTPReply.isPositiveCompletion(port(host, port))) { - __dataConnectionMode = ACTIVE_REMOTE_DATA_CONNECTION_MODE; - __passiveHost = null; - __passivePort = -1; - return true; - } - return false; - } - - /** - * Set the current data connection mode to - * PASSIVE_REMOTE_DATA_CONNECTION_MODE . Use this - * method only for server to server data transfers. - * This method issues a PASV command to the server, telling it to - * open a data port to which the active server will connect to conduct - * data transfers. You must call this method - * before EVERY server to server transfer attempt. The FTPClient will - * NOT automatically continue to issue PASV commands. You also - * must remember to call - * {@link #enterLocalActiveMode enterLocalActiveMode() } if you - * wish to return to the normal data connection mode. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean enterRemotePassiveMode() throws IOException { - if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { - return false; - } - - __dataConnectionMode = PASSIVE_REMOTE_DATA_CONNECTION_MODE; - _parsePassiveModeReply(_replyLines.get(0)); - - return true; - } - - /** - * Returns the hostname or IP address (in the form of a string) returned - * by the server when entering passive mode. If not in passive mode, - * returns null. This method only returns a valid value AFTER a - * data connection has been opened after a call to - * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. - * This is because FTPClient sends a PASV command to the server only - * just before opening a data connection, and not when you call - * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. - * - * @return The passive host name if in passive mode, otherwise null. - */ - public String getPassiveHost() { - return __passiveHost; - } - - /** - * If in passive mode, returns the data port of the passive host. - * This method only returns a valid value AFTER a - * data connection has been opened after a call to - * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. - * This is because FTPClient sends a PASV command to the server only - * just before opening a data connection, and not when you call - * {@link #enterLocalPassiveMode enterLocalPassiveMode()}. - * - * @return The data port of the passive server. If not in passive - * mode, undefined. - */ - public int getPassivePort() { - return __passivePort; - } - - /** - * Returns the current data connection mode (one of the - * _DATA_CONNECTION_MODE constants. - * - * @return The current data connection mode (one of the - * _DATA_CONNECTION_MODE constants. - */ - public int getDataConnectionMode() { - return __dataConnectionMode; - } - - /** - * Get the client port for active mode. - * - * @return The client port for active mode. - */ - private int getActivePort() { - if (__activeMinPort > 0 && __activeMaxPort >= __activeMinPort) { - if (__activeMaxPort == __activeMinPort) { - return __activeMaxPort; - } - // Get a random port between the min and max port range - return __random.nextInt(__activeMaxPort - __activeMinPort + 1) + __activeMinPort; - } else { - // default port - return 0; - } - } - - /** - * Get the host address for active mode; allows the local address to be overridden. - * - * @return __activeExternalHost if non-null, else getLocalAddress() - * @see #setActiveExternalIPAddress(String) - */ - private InetAddress getHostAddress() { - if (__activeExternalHost != null) { - return __activeExternalHost; - } else { - // default local address - return getLocalAddress(); - } - } - - /** - * Get the reported host address for active mode EPRT/PORT commands; - * allows override of {@link #getHostAddress()}. - * - * Useful for FTP Client behind Firewall NAT. - * - * @return __reportActiveExternalHost if non-null, else getHostAddress(); - */ - private InetAddress getReportHostAddress() { - if (__reportActiveExternalHost != null) { - return __reportActiveExternalHost; - } else { - return getHostAddress(); - } - } - - /** - * Set the client side port range in active mode. - * - * @param minPort The lowest available port (inclusive). - * @param maxPort The highest available port (inclusive). - * @since 2.2 - */ - public void setActivePortRange(int minPort, int maxPort) { - this.__activeMinPort = minPort; - this.__activeMaxPort = maxPort; - } - - /** - * Set the external IP address in active mode. - * Useful when there are multiple network cards. - * - * @param ipAddress The external IP address of this machine. - * @throws UnknownHostException if the ipAddress cannot be resolved - * @since 2.2 - */ - public void setActiveExternalIPAddress(String ipAddress) throws UnknownHostException { - this.__activeExternalHost = InetAddress.getByName(ipAddress); - } - - /** - * Set the local IP address to use in passive mode. - * Useful when there are multiple network cards. - * - * @param ipAddress The local IP address of this machine. - * @throws UnknownHostException if the ipAddress cannot be resolved - */ - public void setPassiveLocalIPAddress(String ipAddress) throws UnknownHostException { - this.__passiveLocalHost = InetAddress.getByName(ipAddress); - } - - /** - * Set the local IP address to use in passive mode. - * Useful when there are multiple network cards. - * - * @param inetAddress The local IP address of this machine. - */ - public void setPassiveLocalIPAddress(InetAddress inetAddress) { - this.__passiveLocalHost = inetAddress; - } - - /** - * Set the local IP address in passive mode. - * Useful when there are multiple network cards. - * - * @return The local IP address in passive mode. - */ - public InetAddress getPassiveLocalIPAddress() { - return this.__passiveLocalHost; - } - - /** - * Set the external IP address to report in EPRT/PORT commands in active mode. - * Useful when there are multiple network cards. - * - * @param ipAddress The external IP address of this machine. - * @throws UnknownHostException if the ipAddress cannot be resolved - * @see #getReportHostAddress() - * @since 3.1 - */ - public void setReportActiveExternalIPAddress(String ipAddress) throws UnknownHostException { - this.__reportActiveExternalHost = InetAddress.getByName(ipAddress); - } - - /** - * Sets the file type to be transferred. This should be one of - * FTP.ASCII_FILE_TYPE , FTP.BINARY_FILE_TYPE, - * etc. The file type only needs to be set when you want to change the - * type. After changing it, the new type stays in effect until you change - * it again. The default file type is FTP.ASCII_FILE_TYPE - * if this method is never called. - *
- * The server default is supposed to be ASCII (see RFC 959), however many - * ftp servers default to BINARY. To ensure correct operation with all servers, - * always specify the appropriate file type after connecting to the server. - *
- *

- * N.B. currently calling any connect method will reset the type to - * FTP.ASCII_FILE_TYPE. - * - * @param fileType The _FILE_TYPE constant indcating the - * type of file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean setFileType(int fileType) throws IOException { - if (FTPReply.isPositiveCompletion(type(fileType))) { - __fileType = fileType; - __fileFormat = FTP.NON_PRINT_TEXT_FORMAT; - return true; - } - return false; - } - - /** - * Sets the file type to be transferred and the format. The type should be - * one of FTP.ASCII_FILE_TYPE , - * FTP.BINARY_FILE_TYPE , etc. The file type only needs to - * be set when you want to change the type. After changing it, the new - * type stays in effect until you change it again. The default file type - * is FTP.ASCII_FILE_TYPE if this method is never called. - *
- * The server default is supposed to be ASCII (see RFC 959), however many - * ftp servers default to BINARY. To ensure correct operation with all servers, - * always specify the appropriate file type after connecting to the server. - *
- * The format should be one of the FTP class TEXT_FORMAT - * constants, or if the type is FTP.LOCAL_FILE_TYPE , the - * format should be the byte size for that type. The default format - * is FTP.NON_PRINT_TEXT_FORMAT if this method is never - * called. - *

- * N.B. currently calling any connect method will reset the type to - * FTP.ASCII_FILE_TYPE and the formatOrByteSize to FTP.NON_PRINT_TEXT_FORMAT. - * - * @param fileType The _FILE_TYPE constant indcating the - * type of file. - * @param formatOrByteSize The format of the file (one of the - * _FORMAT constants. In the case of - * LOCAL_FILE_TYPE, the byte size. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean setFileType(int fileType, int formatOrByteSize) throws IOException { - if (FTPReply.isPositiveCompletion(type(fileType, formatOrByteSize))) { - __fileType = fileType; - __fileFormat = formatOrByteSize; - return true; - } - return false; - } - - /** - * Sets the file structure. The default structure is - * FTP.FILE_STRUCTURE if this method is never called - * or if a connect method is called. - * - * @param structure The structure of the file (one of the FTP class - * _STRUCTURE constants). - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean setFileStructure(int structure) throws IOException { - if (FTPReply.isPositiveCompletion(stru(structure))) { - __fileStructure = structure; - return true; - } - return false; - } - - /** - * Sets the transfer mode. The default transfer mode - * FTP.STREAM_TRANSFER_MODE if this method is never called - * or if a connect method is called. - * - * @param mode The new transfer mode to use (one of the FTP class - * _TRANSFER_MODE constants). - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean setFileTransferMode(int mode) throws IOException { - if (FTPReply.isPositiveCompletion(mode(mode))) { - __fileTransferMode = mode; - return true; - } - return false; - } - - /** - * Initiate a server to server file transfer. This method tells the - * server to which the client is connected to retrieve a given file from - * the other server. - * - * @param filename The name of the file to retrieve. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean remoteRetrieve(String filename) throws IOException { - if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE - || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { - return FTPReply.isPositivePreliminary(retr(filename)); - } - return false; - } - - /** - * Initiate a server to server file transfer. This method tells the - * server to which the client is connected to store a file on - * the other server using the given filename. The other server must - * have had a remoteRetrieve issued to it by another - * FTPClient. - * - * @param filename The name to call the file that is to be stored. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean remoteStore(String filename) throws IOException { - if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE - || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { - return FTPReply.isPositivePreliminary(stor(filename)); - } - return false; - } - - /** - * Initiate a server to server file transfer. This method tells the - * server to which the client is connected to store a file on - * the other server using a unique filename based on the given filename. - * The other server must have had a remoteRetrieve issued - * to it by another FTPClient. - * - * @param filename The name on which to base the filename of the file - * that is to be stored. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean remoteStoreUnique(String filename) throws IOException { - if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE - || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { - return FTPReply.isPositivePreliminary(stou(filename)); - } - return false; - } - - /** - * Initiate a server to server file transfer. This method tells the - * server to which the client is connected to store a file on - * the other server using a unique filename. - * The other server must have had a remoteRetrieve issued - * to it by another FTPClient. Many FTP servers require that a base - * filename be given from which the unique filename can be derived. For - * those servers use the other version of remoteStoreUnique - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean remoteStoreUnique() throws IOException { - if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE - || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { - return FTPReply.isPositivePreliminary(stou()); - } - return false; - } - - // For server to server transfers - - /** - * Initiate a server to server file transfer. This method tells the - * server to which the client is connected to append to a given file on - * the other server. The other server must have had a - * remoteRetrieve issued to it by another FTPClient. - * - * @param filename The name of the file to be appended to, or if the - * file does not exist, the name to call the file being stored. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean remoteAppend(String filename) throws IOException { - if (__dataConnectionMode == ACTIVE_REMOTE_DATA_CONNECTION_MODE - || __dataConnectionMode == PASSIVE_REMOTE_DATA_CONNECTION_MODE) { - return FTPReply.isPositivePreliminary(appe(filename)); - } - return false; - } - - /** - * There are a few FTPClient methods that do not complete the - * entire sequence of FTP commands to complete a transaction. These - * commands require some action by the programmer after the reception - * of a positive intermediate command. After the programmer's code - * completes its actions, it must call this method to receive - * the completion reply from the server and verify the success of the - * entire transaction. - *

- * For example, - *

-   * InputStream input;
-   * OutputStream output;
-   * input  = new FileInputStream("foobaz.txt");
-   * output = ftp.storeFileStream("foobar.txt")
-   * if(!FTPReply.isPositiveIntermediate(ftp.getReplyCode())) {
-   *     input.close();
-   *     output.close();
-   *     ftp.logout();
-   *     ftp.disconnect();
-   *     System.err.println("File transfer failed.");
-   *     System.exit(1);
-   * }
-   * Util.copyStream(input, output);
-   * input.close();
-   * output.close();
-   * // Must call completePendingCommand() to finish command.
-   * if(!ftp.completePendingCommand()) {
-   *     ftp.logout();
-   *     ftp.disconnect();
-   *     System.err.println("File transfer failed.");
-   *     System.exit(1);
-   * }
-   * 
- * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean completePendingCommand() throws IOException { - return FTPReply.isPositiveCompletion(getReply()); - } - - /** - * Retrieves a named file from the server and writes it to the given - * OutputStream. This method does NOT close the given OutputStream. - * If the current file type is ASCII, line separators in the file are - * converted to the local representation. - *

- * Note: if you have used {@link #setRestartOffset(long)}, - * the file data will start from the selected offset. - * - * @param remote The name of the remote file. - * @param local The local OutputStream to which to write the file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws CopyStreamException If an I/O error occurs while actually - * transferring the file. The CopyStreamException allows you to - * determine the number of bytes transferred and the IOException - * causing the error. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean retrieveFile(String remote, OutputStream local) throws IOException { - return _retrieveFile(FTPCmd.RETR.getCommand(), remote, local); - } - - /** - * @param command the command to get - * @param remote the remote file name - * @param local the local file name - * @return true if successful - * @throws IOException on error - * @since 3.1 - */ - protected boolean _retrieveFile(String command, String remote, OutputStream local) - throws IOException { - Socket socket = _openDataConnection_(command, remote); - - if (socket == null) { - return false; - } - - final InputStream input; - if (__fileType == ASCII_FILE_TYPE) { - input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); - } else { - input = getBufferedInputStream(socket.getInputStream()); - } - - CSL csl = null; - if (__controlKeepAliveTimeout > 0) { - csl = new CSL(this, __controlKeepAliveTimeout, __controlKeepAliveReplyTimeout); - } - - // Treat everything else as binary for now - try { - Util.copyStream(input, local, getBufferSize(), CopyStreamEvent.UNKNOWN_STREAM_SIZE, - __mergeListeners(csl), false); - } finally { - Util.closeQuietly(input); - Util.closeQuietly(socket); - if (csl != null) { - csl.cleanUp(); // fetch any outstanding keepalive replies - } - } - - // Get the transfer response - boolean ok = completePendingCommand(); - return ok; - } - - /** - * Returns an InputStream from which a named file from the server - * can be read. If the current file type is ASCII, the returned - * InputStream will convert line separators in the file to - * the local representation. You must close the InputStream when you - * finish reading from it. The InputStream itself will take care of - * closing the parent data connection socket upon being closed. - *

- * To finalize the file transfer you must call - * {@link #completePendingCommand completePendingCommand } and - * check its return value to verify success. - * If this is not done, subsequent commands may behave unexpectedly. - *

- * Note: if you have used {@link #setRestartOffset(long)}, - * the file data will start from the selected offset. - * - * @param remote The name of the remote file. - * @return An InputStream from which the remote file can be read. If - * the data connection cannot be opened (e.g., the file does not - * exist), null is returned (in which case you may check the reply - * code to determine the exact reason for failure). - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public InputStream retrieveFileStream(String remote) throws IOException { - return _retrieveFileStream(FTPCmd.RETR.getCommand(), remote); - } - - /** - * @param command the command to send - * @param remote the remote file name - * @return the stream from which to read the file - * @throws IOException on error - * @since 3.1 - */ - protected InputStream _retrieveFileStream(String command, String remote) throws IOException { - Socket socket = _openDataConnection_(command, remote); - - if (socket == null) { - return null; - } - - final InputStream input; - if (__fileType == ASCII_FILE_TYPE) { - // We buffer ascii transfers because the buffering has to - // be interposed between FromNetASCIIOutputSream and the underlying - // socket input stream. We don't buffer binary transfers - // because we don't want to impose a buffering policy on the - // programmer if possible. Programmers can decide on their - // own if they want to wrap the SocketInputStream we return - // for file types other than ASCII. - input = new FromNetASCIIInputStream(getBufferedInputStream(socket.getInputStream())); - } else { - input = socket.getInputStream(); - } - return new SocketInputStream(socket, input); - } - - /** - * Stores a file on the server using the given name and taking input - * from the given InputStream. This method does NOT close the given - * InputStream. If the current file type is ASCII, line separators in - * the file are transparently converted to the NETASCII format (i.e., - * you should not attempt to create a special InputStream to do this). - * - * @param remote The name to give the remote file. - * @param local The local InputStream from which to read the file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws CopyStreamException If an I/O error occurs while actually - * transferring the file. The CopyStreamException allows you to - * determine the number of bytes transferred and the IOException - * causing the error. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean storeFile(String remote, InputStream local) throws IOException { - return __storeFile(FTPCmd.STOR, remote, local); - } - - OnFtpInputStreamListener mListener; - - /** - * 含有事件的Ftp上传文件 - * - * @throws IOException - */ - public boolean storeFile(String remote, InputStream local, OnFtpInputStreamListener listener) - throws IOException { - mListener = listener; - return __storeFile(FTPCmd.STOR, remote, local); - } - - /** - * Returns an OutputStream through which data can be written to store - * a file on the server using the given name. If the current file type - * is ASCII, the returned OutputStream will convert line separators in - * the file to the NETASCII format (i.e., you should not attempt to - * create a special OutputStream to do this). You must close the - * OutputStream when you finish writing to it. The OutputStream itself - * will take care of closing the parent data connection socket upon being - * closed. - *

- * To finalize the file transfer you must call - * {@link #completePendingCommand completePendingCommand } and - * check its return value to verify success. - * If this is not done, subsequent commands may behave unexpectedly. - * - * @param remote The name to give the remote file. - * @return An OutputStream through which the remote file can be written. If - * the data connection cannot be opened (e.g., the file does not - * exist), null is returned (in which case you may check the reply - * code to determine the exact reason for failure). - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public OutputStream storeFileStream(String remote) throws IOException { - return __storeFileStream(FTPCmd.STOR, remote); - } - - /** - * Appends to a file on the server with the given name, taking input - * from the given InputStream. This method does NOT close the given - * InputStream. If the current file type is ASCII, line separators in - * the file are transparently converted to the NETASCII format (i.e., - * you should not attempt to create a special InputStream to do this). - * - * @param remote The name of the remote file. - * @param local The local InputStream from which to read the data to - * be appended to the remote file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws CopyStreamException If an I/O error occurs while actually - * transferring the file. The CopyStreamException allows you to - * determine the number of bytes transferred and the IOException - * causing the error. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean appendFile(String remote, InputStream local) throws IOException { - return __storeFile(FTPCmd.APPE, remote, local); - } - - /** - * Returns an OutputStream through which data can be written to append - * to a file on the server with the given name. If the current file type - * is ASCII, the returned OutputStream will convert line separators in - * the file to the NETASCII format (i.e., you should not attempt to - * create a special OutputStream to do this). You must close the - * OutputStream when you finish writing to it. The OutputStream itself - * will take care of closing the parent data connection socket upon being - * closed. - *

- * To finalize the file transfer you must call - * {@link #completePendingCommand completePendingCommand } and - * check its return value to verify success. - * If this is not done, subsequent commands may behave unexpectedly. - * - * @param remote The name of the remote file. - * @return An OutputStream through which the remote file can be appended. - * If the data connection cannot be opened (e.g., the file does not - * exist), null is returned (in which case you may check the reply - * code to determine the exact reason for failure). - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public OutputStream appendFileStream(String remote) throws IOException { - return __storeFileStream(FTPCmd.APPE, remote); - } - - /** - * Stores a file on the server using a unique name derived from the - * given name and taking input - * from the given InputStream. This method does NOT close the given - * InputStream. If the current file type is ASCII, line separators in - * the file are transparently converted to the NETASCII format (i.e., - * you should not attempt to create a special InputStream to do this). - * - * @param remote The name on which to base the unique name given to - * the remote file. - * @param local The local InputStream from which to read the file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws CopyStreamException If an I/O error occurs while actually - * transferring the file. The CopyStreamException allows you to - * determine the number of bytes transferred and the IOException - * causing the error. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean storeUniqueFile(String remote, InputStream local) throws IOException { - return __storeFile(FTPCmd.STOU, remote, local); - } - - /** - * Returns an OutputStream through which data can be written to store - * a file on the server using a unique name derived from the given name. - * If the current file type - * is ASCII, the returned OutputStream will convert line separators in - * the file to the NETASCII format (i.e., you should not attempt to - * create a special OutputStream to do this). You must close the - * OutputStream when you finish writing to it. The OutputStream itself - * will take care of closing the parent data connection socket upon being - * closed. - *

- * To finalize the file transfer you must call - * {@link #completePendingCommand completePendingCommand } and - * check its return value to verify success. - * If this is not done, subsequent commands may behave unexpectedly. - * - * @param remote The name on which to base the unique name given to - * the remote file. - * @return An OutputStream through which the remote file can be written. If - * the data connection cannot be opened (e.g., the file does not - * exist), null is returned (in which case you may check the reply - * code to determine the exact reason for failure). - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public OutputStream storeUniqueFileStream(String remote) throws IOException { - return __storeFileStream(FTPCmd.STOU, remote); - } - - /** - * Stores a file on the server using a unique name assigned by the - * server and taking input from the given InputStream. This method does - * NOT close the given - * InputStream. If the current file type is ASCII, line separators in - * the file are transparently converted to the NETASCII format (i.e., - * you should not attempt to create a special InputStream to do this). - * - * @param local The local InputStream from which to read the file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws CopyStreamException If an I/O error occurs while actually - * transferring the file. The CopyStreamException allows you to - * determine the number of bytes transferred and the IOException - * causing the error. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean storeUniqueFile(InputStream local) throws IOException { - return __storeFile(FTPCmd.STOU, null, local); - } - - /** - * Returns an OutputStream through which data can be written to store - * a file on the server using a unique name assigned by the server. - * If the current file type - * is ASCII, the returned OutputStream will convert line separators in - * the file to the NETASCII format (i.e., you should not attempt to - * create a special OutputStream to do this). You must close the - * OutputStream when you finish writing to it. The OutputStream itself - * will take care of closing the parent data connection socket upon being - * closed. - *

- * To finalize the file transfer you must call - * {@link #completePendingCommand completePendingCommand } and - * check its return value to verify success. - * If this is not done, subsequent commands may behave unexpectedly. - * - * @return An OutputStream through which the remote file can be written. If - * the data connection cannot be opened (e.g., the file does not - * exist), null is returned (in which case you may check the reply - * code to determine the exact reason for failure). - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public OutputStream storeUniqueFileStream() throws IOException { - return __storeFileStream(FTPCmd.STOU, null); - } - - /** - * Reserve a number of bytes on the server for the next file transfer. - * - * @param bytes The number of bytes which the server should allocate. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean allocate(int bytes) throws IOException { - return FTPReply.isPositiveCompletion(allo(bytes)); - } - - /** - * Query the server for supported features. The server may reply with a list of server-supported - * exensions. - * For example, a typical client-server interaction might be (from RFC 2389): - *

-   * C> feat
-   * S> 211-Extensions supported:
-   * S>  MLST size*;create;modify*;perm;media-type
-   * S>  SIZE
-   * S>  COMPRESSION
-   * S>  MDTM
-   * S> 211 END
-   * 
- * - * @return True if successfully completed, false if not. - * @throws IOException on error - * @see http://www.faqs.org/rfcs/rfc2389.html - * @since 2.2 - */ - public boolean features() throws IOException { - return FTPReply.isPositiveCompletion(feat()); - } - - /** - * Query the server for a supported feature, and returns its values (if any). - * Caches the parsed response to avoid resending the command repeatedly. - * - * @param feature the feature to check - * @return if the feature is present, returns the feature values (empty array if none) - * Returns {@code null} if the feature is not found or the command failed. - * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. - * @throws IOException on error - * @since 3.0 - */ - public String[] featureValues(String feature) throws IOException { - if (!initFeatureMap()) { - return null; - } - Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); - if (entries != null) { - return entries.toArray(new String[entries.size()]); - } - return null; - } - - /** - * Query the server for a supported feature, and returns the its value (if any). - * Caches the parsed response to avoid resending the command repeatedly. - * - * @param feature the feature to check - * @return if the feature is present, returns the feature value or the empty string - * if the feature exists but has no value. - * Returns {@code null} if the feature is not found or the command failed. - * Check {@link #getReplyCode()} or {@link #getReplyString()} if so. - * @throws IOException on error - * @since 3.0 - */ - public String featureValue(String feature) throws IOException { - String[] values = featureValues(feature); - if (values != null) { - return values[0]; - } - return null; - } - - /** - * Query the server for a supported feature. - * Caches the parsed response to avoid resending the command repeatedly. - * - * @param feature the name of the feature; it is converted to upper case. - * @return {@code true} if the feature is present, {@code false} if the feature is not present - * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link - * #getReplyString()} - * if it is necessary to distinguish these cases. - * @throws IOException on error - * @since 3.0 - */ - public boolean hasFeature(String feature) throws IOException { - if (!initFeatureMap()) { - return false; - } - return __featuresMap.containsKey(feature.toUpperCase(Locale.ENGLISH)); - } - - /** - * Query the server for a supported feature with particular value, - * for example "AUTH SSL" or "AUTH TLS". - * Caches the parsed response to avoid resending the command repeatedly. - * - * @param feature the name of the feature; it is converted to upper case. - * @param value the value to find. - * @return {@code true} if the feature is present, {@code false} if the feature is not present - * or the {@link #feat()} command failed. Check {@link #getReplyCode()} or {@link - * #getReplyString()} - * if it is necessary to distinguish these cases. - * @throws IOException on error - * @since 3.0 - */ - public boolean hasFeature(String feature, String value) throws IOException { - if (!initFeatureMap()) { - return false; - } - Set entries = __featuresMap.get(feature.toUpperCase(Locale.ENGLISH)); - if (entries != null) { - return entries.contains(value); - } - return false; - } - - /* - * Create the feature map if not already created. - */ - private boolean initFeatureMap() throws IOException { - if (__featuresMap == null) { - // Don't create map here, because next line may throw exception - final int replyCode = feat(); - if (replyCode == FTPReply.NOT_LOGGED_IN) { // 503 - return false; // NET-518; don't create empy map - } - boolean success = FTPReply.isPositiveCompletion(replyCode); - // we init the map here, so we don't keep trying if we know the command will fail - __featuresMap = new HashMap>(); - if (!success) { - return false; - } - for (String l : getReplyStrings()) { - if (l.startsWith(" ")) { // it's a FEAT entry - String key; - String value = ""; - int varsep = l.indexOf(' ', 1); - if (varsep > 0) { - key = l.substring(1, varsep); - value = l.substring(varsep + 1); - } else { - key = l.substring(1); - } - key = key.toUpperCase(Locale.ENGLISH); - Set entries = __featuresMap.get(key); - if (entries == null) { - entries = new HashSet(); - __featuresMap.put(key, entries); - } - entries.add(value); - } - } - } - return true; - } - - /** - * Reserve space on the server for the next file transfer. - * - * @param bytes The number of bytes which the server should allocate. - * @param recordSize The size of a file record. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean allocate(int bytes, int recordSize) throws IOException { - return FTPReply.isPositiveCompletion(allo(bytes, recordSize)); - } - - /** - * Issue a command and wait for the reply. - *

- * Should only be used with commands that return replies on the - * command channel - do not use for LIST, NLST, MLSD etc. - * - * @param command The command to invoke - * @param params The parameters string, may be {@code null} - * @return True if successfully completed, false if not, in which case - * call {@link #getReplyCode()} or {@link #getReplyString()} - * to get the reason. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.0 - */ - public boolean doCommand(String command, String params) throws IOException { - return FTPReply.isPositiveCompletion(sendCommand(command, params)); - } - - /** - * Issue a command and wait for the reply, returning it as an array of strings. - *

- * Should only be used with commands that return replies on the - * command channel - do not use for LIST, NLST, MLSD etc. - * - * @param command The command to invoke - * @param params The parameters string, may be {@code null} - * @return The array of replies, or {@code null} if the command failed, in which case - * call {@link #getReplyCode()} or {@link #getReplyString()} - * to get the reason. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.0 - */ - public String[] doCommandAsStrings(String command, String params) throws IOException { - boolean success = FTPReply.isPositiveCompletion(sendCommand(command, params)); - if (success) { - return getReplyStrings(); - } else { - return null; - } - } - - /** - * Get file details using the MLST command - * - * @param pathname the file or directory to list, may be {@code null} - * @return the file details, may be {@code null} - * @throws IOException on error - * @since 3.0 - */ - public FTPFile mlistFile(String pathname) throws IOException { - boolean success = FTPReply.isPositiveCompletion(sendCommand(FTPCmd.MLST, pathname)); - if (success) { - String reply = getReplyStrings()[1]; - /* check the response makes sense. - * Must have space before fact(s) and between fact(s) and filename - * Fact(s) can be absent, so at least 3 chars are needed. - */ - if (reply.length() < 3 || reply.charAt(0) != ' ') { - throw new MalformedServerReplyException("Invalid server reply (MLST): '" + reply + "'"); - } - String entry = reply.substring(1); // skip leading space for parser - return MLSxEntryParser.parseEntry(entry); - } else { - return null; - } - } - - /** - * Generate a directory listing for the current directory using the MLSD command. - * - * @return the array of file entries - * @throws IOException on error - * @since 3.0 - */ - public FTPFile[] mlistDir() throws IOException { - return mlistDir(null); - } - - /** - * Generate a directory listing using the MLSD command. - * - * @param pathname the directory name, may be {@code null} - * @return the array of file entries - * @throws IOException on error - * @since 3.0 - */ - public FTPFile[] mlistDir(String pathname) throws IOException { - FTPListParseEngine engine = initiateMListParsing(pathname); - return engine.getFiles(); - } - - /** - * Generate a directory listing using the MLSD command. - * - * @param pathname the directory name, may be {@code null} - * @param filter the filter to apply to the responses - * @return the array of file entries - * @throws IOException on error - * @since 3.0 - */ - public FTPFile[] mlistDir(String pathname, FTPFileFilter filter) throws IOException { - FTPListParseEngine engine = initiateMListParsing(pathname); - return engine.getFiles(filter); - } - - /** - * Restart a STREAM_TRANSFER_MODE file transfer starting - * from the given offset. This will only work on FTP servers supporting - * the REST comand for the stream transfer mode. However, most FTP - * servers support this. Any subsequent file transfer will start - * reading or writing the remote file from the indicated offset. - * - * @param offset The offset into the remote file at which to start the - * next file transfer. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @since 3.1 (changed from private to protected) - */ - protected boolean restart(long offset) throws IOException { - __restartOffset = 0; - return FTPReply.isPositiveIntermediate(rest(Long.toString(offset))); - } - - /** - * Sets the restart offset for file transfers. - *

- * The restart command is not sent to the server immediately. - * It is sent when a data connection is created as part of a - * subsequent command. - * The restart marker is reset to zero after use. - *

- *

- * Note: This method should only be invoked immediately prior to - * the transfer to which it applies. - * - * @param offset The offset into the remote file at which to start the - * next file transfer. This must be a value greater than or - * equal to zero. - */ - public void setRestartOffset(long offset) { - if (offset >= 0) { - __restartOffset = offset; - } - } - - /** - * Fetches the restart offset. - * - * @return offset The offset into the remote file at which to start the - * next file transfer. - */ - public long getRestartOffset() { - return __restartOffset; - } - - /** - * Renames a remote file. - * - * @param from The name of the remote file to rename. - * @param to The new name of the remote file. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean rename(String from, String to) throws IOException { - if (!FTPReply.isPositiveIntermediate(rnfr(from))) { - return false; - } - - return FTPReply.isPositiveCompletion(rnto(to)); - } - - /** - * Abort a transfer in progress. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean abort() throws IOException { - return FTPReply.isPositiveCompletion(abor()); - } - - /** - * Deletes a file on the FTP server. - * - * @param pathname The pathname of the file to be deleted. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean deleteFile(String pathname) throws IOException { - return FTPReply.isPositiveCompletion(dele(pathname)); - } - - /** - * Removes a directory on the FTP server (if empty). - * - * @param pathname The pathname of the directory to remove. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean removeDirectory(String pathname) throws IOException { - return FTPReply.isPositiveCompletion(rmd(pathname)); - } - - /** - * Creates a new subdirectory on the FTP server in the current directory - * (if a relative pathname is given) or where specified (if an absolute - * pathname is given). - * - * @param pathname The pathname of the directory to create. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean makeDirectory(String pathname) throws IOException { - return FTPReply.isPositiveCompletion(mkd(pathname)); - } - - /** - * Returns the pathname of the current working directory. - * - * @return The pathname of the current working directory. If it cannot - * be obtained, returns null. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String printWorkingDirectory() throws IOException { - if (pwd() != FTPReply.PATHNAME_CREATED) { - return null; - } - - return __parsePathname(_replyLines.get(_replyLines.size() - 1)); - } - - /** - * Send a site specific command. - * - * @param arguments The site specific command and arguments. - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean sendSiteCommand(String arguments) throws IOException { - return FTPReply.isPositiveCompletion(site(arguments)); - } - - /** - * Fetches the system type from the server and returns the string. - * This value is cached for the duration of the connection after the - * first call to this method. In other words, only the first time - * that you invoke this method will it issue a SYST command to the - * FTP server. FTPClient will remember the value and return the - * cached value until a call to disconnect. - *

- * If the SYST command fails, and the system property - * {@link #FTP_SYSTEM_TYPE_DEFAULT} is defined, then this is used instead. - * - * @return The system type obtained from the server. Never null. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server (and the default - * system type property is not defined) - * @since 2.2 - */ - public String getSystemType() throws IOException { - //if (syst() == FTPReply.NAME_SYSTEM_TYPE) - // Technically, we should expect a NAME_SYSTEM_TYPE response, but - // in practice FTP servers deviate, so we soften the condition to - // a positive completion. - if (__systemName == null) { - if (FTPReply.isPositiveCompletion(syst())) { - // Assume that response is not empty here (cannot be null) - __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); - } else { - // Check if the user has provided a default for when the SYST command fails - String systDefault = System.getProperty(FTP_SYSTEM_TYPE_DEFAULT); - if (systDefault != null) { - __systemName = systDefault; - } else { - throw new IOException("Unable to determine system type - response: " + getReplyString()); - } - } - } - return __systemName; - } - - /** - * Fetches the system help information from the server and returns the - * full string. - * - * @return The system help string obtained from the server. null if the - * information could not be obtained. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String listHelp() throws IOException { - if (FTPReply.isPositiveCompletion(help())) { - return getReplyString(); - } - return null; - } - - /** - * Fetches the help information for a given command from the server and - * returns the full string. - * - * @param command The command on which to ask for help. - * @return The command help string obtained from the server. null if the - * information could not be obtained. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String listHelp(String command) throws IOException { - if (FTPReply.isPositiveCompletion(help(command))) { - return getReplyString(); - } - return null; - } - - /** - * Sends a NOOP command to the FTP server. This is useful for preventing - * server timeouts. - * - * @return True if successfully completed, false if not. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public boolean sendNoOp() throws IOException { - return FTPReply.isPositiveCompletion(noop()); - } - - /** - * Obtain a list of filenames in a directory (or just the name of a given - * file, which is not particularly useful). This information is obtained - * through the NLST command. If the given pathname is a directory and - * contains no files, a zero length array is returned only - * if the FTP server returned a positive completion code, otherwise - * null is returned (the FTP server returned a 550 error No files found.). - * If the directory is not empty, an array of filenames in the directory is - * returned. If the pathname corresponds - * to a file, only that file will be listed. The server may or may not - * expand glob expressions. - * - * @param pathname The file or directory to list. - * Warning: the server may treat a leading '-' as an - * option introducer. If so, try using an absolute path, - * or prefix the path with ./ (unix style servers). - * Some servers may support "--" as meaning end of options, - * in which case "-- -xyz" should work. - * @return The list of filenames contained in the given path. null if - * the list could not be obtained. If there are no filenames in - * the directory, a zero-length array is returned. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String[] listNames(String pathname) throws IOException { - Socket socket = _openDataConnection_(FTPCmd.NLST, getListArguments(pathname)); - - if (socket == null) { - return null; - } - - BufferedReader reader = - new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); - - ArrayList results = new ArrayList(); - String line; - while ((line = reader.readLine()) != null) { - results.add(line); - } - - reader.close(); - socket.close(); - - if (completePendingCommand()) { - String[] names = new String[results.size()]; - return results.toArray(names); - } - - return null; - } - - /** - * Obtain a list of filenames in the current working directory - * This information is obtained through the NLST command. If the current - * directory contains no files, a zero length array is returned only - * if the FTP server returned a positive completion code, otherwise, - * null is returned (the FTP server returned a 550 error No files found.). - * If the directory is not empty, an array of filenames in the directory is - * returned. - * - * @return The list of filenames contained in the current working - * directory. null if the list could not be obtained. - * If there are no filenames in the directory, a zero-length array - * is returned. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String[] listNames() throws IOException { - return listNames(null); - } - - /** - * Using the default system autodetect mechanism, obtain a - * list of file information for the current working directory - * or for just a single file. - *

- * This information is obtained through the LIST command. The contents of - * the returned array is determined by the FTPFileEntryParser - * used. - *

- * N.B. the LIST command does not generally return very precise timestamps. - * For recent files, the response usually contains hours and minutes (not seconds). - * For older files, the output may only contain a date. - * If the server supports it, the MLSD command returns timestamps with a precision - * of seconds, and may include milliseconds. See {@link #mlistDir()} - * - * @param pathname The file or directory to list. Since the server may - * or may not expand glob expressions, using them here - * is not recommended and may well cause this method to - * fail. - * Also, some servers treat a leading '-' as being an option. - * To avoid this interpretation, use an absolute pathname - * or prefix the pathname with ./ (unix style servers). - * Some servers may support "--" as meaning end of options, - * in which case "-- -xyz" should work. - * @return The list of file information contained in the given path in - * the format determined by the autodetection mechanism - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection - * as a result of the client being idle or some other - * reason causing the server to send FTP reply code 421. - * This exception may be caught either as an IOException - * or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply - * from the server. - * @throws ParserInitializationException Thrown if the - * parserKey - * parameter cannot be - * resolved by the selected parser factory. - * In the DefaultFTPEntryParserFactory, this will - * happen when parserKey is neither - * the fully qualified class name of a class - * implementing the interface - * FTPFileEntryParser - * nor a string containing one of the recognized keys - * mapping to such a parser or if class loader - * security issues prevent its being loaded. - * @see DefaultFTPFileEntryParserFactory - * @see FTPFileEntryParserFactory - * @see FTPFileEntryParser - */ - public FTPFile[] listFiles(String pathname) throws IOException { - FTPListParseEngine engine = initiateListParsing((String) null, pathname); - return engine.getFiles(); - } - - /** - * Using the default system autodetect mechanism, obtain a - * list of file information for the current working directory. - *

- * This information is obtained through the LIST command. The contents of - * the returned array is determined by the FTPFileEntryParser - * used. - *

- * N.B. the LIST command does not generally return very precise timestamps. - * For recent files, the response usually contains hours and minutes (not seconds). - * For older files, the output may only contain a date. - * If the server supports it, the MLSD command returns timestamps with a precision - * of seconds, and may include milliseconds. See {@link #mlistDir()} - * - * @return The list of file information contained in the current directory - * in the format determined by the autodetection mechanism. - *

- * NOTE: This array may contain null members if any of the - * individual file listings failed to parse. The caller should - * check each entry for null before referencing it. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection - * as a result of the client being idle or some other - * reason causing the server to send FTP reply code 421. - * This exception may be caught either as an IOException - * or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply - * from the server. - * @throws ParserInitializationException Thrown if the - * parserKey - * parameter cannot be - * resolved by the selected parser factory. - * In the DefaultFTPEntryParserFactory, this will - * happen when parserKey is neither - * the fully qualified class name of a class - * implementing the interface - * FTPFileEntryParser - * nor a string containing one of the recognized keys - * mapping to such a parser or if class loader - * security issues prevent its being loaded. - * @see DefaultFTPFileEntryParserFactory - * @see FTPFileEntryParserFactory - * @see FTPFileEntryParser - */ - public FTPFile[] listFiles() throws IOException { - return listFiles((String) null); - } - - /** - * Version of {@link #listFiles(String)} which allows a filter to be provided. - * For example: listFiles("site", FTPFileFilters.DIRECTORY); - * - * @param pathname the initial path, may be null - * @param filter the filter, non-null - * @return the list of FTPFile entries. - * @throws IOException on error - * @since 2.2 - */ - public FTPFile[] listFiles(String pathname, FTPFileFilter filter) throws IOException { - FTPListParseEngine engine = initiateListParsing((String) null, pathname); - return engine.getFiles(filter); - } - - /** - * Using the default system autodetect mechanism, obtain a - * list of directories contained in the current working directory. - *

- * This information is obtained through the LIST command. The contents of - * the returned array is determined by the FTPFileEntryParser - * used. - *

- * N.B. the LIST command does not generally return very precise timestamps. - * For recent files, the response usually contains hours and minutes (not seconds). - * For older files, the output may only contain a date. - * If the server supports it, the MLSD command returns timestamps with a precision - * of seconds, and may include milliseconds. See {@link #mlistDir()} - * - * @return The list of directories contained in the current directory - * in the format determined by the autodetection mechanism. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection - * as a result of the client being idle or some other - * reason causing the server to send FTP reply code 421. - * This exception may be caught either as an IOException - * or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply - * from the server. - * @throws ParserInitializationException Thrown if the - * parserKey - * parameter cannot be - * resolved by the selected parser factory. - * In the DefaultFTPEntryParserFactory, this will - * happen when parserKey is neither - * the fully qualified class name of a class - * implementing the interface - * FTPFileEntryParser - * nor a string containing one of the recognized keys - * mapping to such a parser or if class loader - * security issues prevent its being loaded. - * @see DefaultFTPFileEntryParserFactory - * @see FTPFileEntryParserFactory - * @see FTPFileEntryParser - * @since 3.0 - */ - public FTPFile[] listDirectories() throws IOException { - return listDirectories((String) null); - } - - /** - * Using the default system autodetect mechanism, obtain a - * list of directories contained in the specified directory. - *

- * This information is obtained through the LIST command. The contents of - * the returned array is determined by the FTPFileEntryParser - * used. - *

- * N.B. the LIST command does not generally return very precise timestamps. - * For recent files, the response usually contains hours and minutes (not seconds). - * For older files, the output may only contain a date. - * If the server supports it, the MLSD command returns timestamps with a precision - * of seconds, and may include milliseconds. See {@link #mlistDir()} - * - * @param parent the starting directory - * @return The list of directories contained in the specified directory - * in the format determined by the autodetection mechanism. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection - * as a result of the client being idle or some other - * reason causing the server to send FTP reply code 421. - * This exception may be caught either as an IOException - * or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply - * from the server. - * @throws ParserInitializationException Thrown if the - * parserKey - * parameter cannot be - * resolved by the selected parser factory. - * In the DefaultFTPEntryParserFactory, this will - * happen when parserKey is neither - * the fully qualified class name of a class - * implementing the interface - * FTPFileEntryParser - * nor a string containing one of the recognized keys - * mapping to such a parser or if class loader - * security issues prevent its being loaded. - * @see DefaultFTPFileEntryParserFactory - * @see FTPFileEntryParserFactory - * @see FTPFileEntryParser - * @since 3.0 - */ - public FTPFile[] listDirectories(String parent) throws IOException { - return listFiles(parent, FTPFileFilters.DIRECTORIES); - } - - /** - * Using the default autodetect mechanism, initialize an FTPListParseEngine - * object containing a raw file information for the current working - * directory on the server - * This information is obtained through the LIST command. This object - * is then capable of being iterated to return a sequence of FTPFile - * objects with information filled in by the - * FTPFileEntryParser used. - *

- * This method differs from using the listFiles() methods in that - * expensive FTPFile objects are not created until needed which may be - * an advantage on large lists. - * - * @return A FTPListParseEngine object that holds the raw information and - * is capable of providing parsed FTPFile objects, one for each file - * containing information contained in the given path in the format - * determined by the parser parameter. Null will be - * returned if a data connection cannot be opened. If the current working - * directory contains no files, an empty array will be the return. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @throws ParserInitializationException Thrown if the - * autodetect mechanism cannot - * resolve the type of system we are connected with. - * @see FTPListParseEngine - */ - public FTPListParseEngine initiateListParsing() throws IOException { - return initiateListParsing((String) null); - } - - /** - * Using the default autodetect mechanism, initialize an FTPListParseEngine - * object containing a raw file information for the supplied directory. - * This information is obtained through the LIST command. This object - * is then capable of being iterated to return a sequence of FTPFile - * objects with information filled in by the - * FTPFileEntryParser used. - *

- * The server may or may not expand glob expressions. You should avoid - * using glob expressions because the return format for glob listings - * differs from server to server and will likely cause this method to fail. - *

- * This method differs from using the listFiles() methods in that - * expensive FTPFile objects are not created until needed which may be - * an advantage on large lists. - * - *

-   *    FTPClient f=FTPClient();
-   *    f.connect(server);
-   *    f.login(username, password);
-   *    FTPListParseEngine engine = f.initiateListParsing(directory);
-   *
-   *    while (engine.hasNext()) {
-   *       FTPFile[] files = engine.getNext(25);  // "page size" you want
-   *       //do whatever you want with these files, display them, etc.
-   *       //expensive FTPFile objects not created until needed.
-   *    }
-   * 
- * - * @param pathname the starting directory - * @return A FTPListParseEngine object that holds the raw information and - * is capable of providing parsed FTPFile objects, one for each file - * containing information contained in the given path in the format - * determined by the parser parameter. Null will be - * returned if a data connection cannot be opened. If the current working - * directory contains no files, an empty array will be the return. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @throws ParserInitializationException Thrown if the - * autodetect mechanism cannot - * resolve the type of system we are connected with. - * @see FTPListParseEngine - */ - public FTPListParseEngine initiateListParsing(String pathname) throws IOException { - return initiateListParsing((String) null, pathname); - } - - /** - * Using the supplied parser key, initialize an FTPListParseEngine - * object containing a raw file information for the supplied directory. - * This information is obtained through the LIST command. This object - * is then capable of being iterated to return a sequence of FTPFile - * objects with information filled in by the - * FTPFileEntryParser used. - *

- * The server may or may not expand glob expressions. You should avoid - * using glob expressions because the return format for glob listings - * differs from server to server and will likely cause this method to fail. - *

- * This method differs from using the listFiles() methods in that - * expensive FTPFile objects are not created until needed which may be - * an advantage on large lists. - * - * @param parserKey A string representing a designated code or fully-qualified - * class name of an FTPFileEntryParser that should be - * used to parse each server file listing. - * May be {@code null}, in which case the code checks first - * the system property {@link #FTP_SYSTEM_TYPE}, and if that is - * not defined the SYST command is used to provide the value. - * To allow for arbitrary system types, the return from the - * SYST command is used to look up an alias for the type in the - * {@link #SYSTEM_TYPE_PROPERTIES} properties file if it is available. - * @param pathname the starting directory - * @return A FTPListParseEngine object that holds the raw information and - * is capable of providing parsed FTPFile objects, one for each file - * containing information contained in the given path in the format - * determined by the parser parameter. Null will be - * returned if a data connection cannot be opened. If the current working - * directory contains no files, an empty array will be the return. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @throws ParserInitializationException Thrown if the - * parserKey - * parameter cannot be - * resolved by the selected parser factory. - * In the DefaultFTPEntryParserFactory, this will - * happen when parserKey is neither - * the fully qualified class name of a class - * implementing the interface - * FTPFileEntryParser - * nor a string containing one of the recognized keys - * mapping to such a parser or if class loader - * security issues prevent its being loaded. - * @see FTPListParseEngine - */ - public FTPListParseEngine initiateListParsing(String parserKey, String pathname) - throws IOException { - __createParser(parserKey); // create and cache parser - return initiateListParsing(__entryParser, pathname); - } - - // package access for test purposes - void __createParser(String parserKey) throws IOException { - // We cache the value to avoid creation of a new object every - // time a file listing is generated. - // Note: we don't check against a null parserKey (NET-544) - if (__entryParser == null || (parserKey != null && !__entryParserKey.equals(parserKey))) { - if (null != parserKey) { - // if a parser key was supplied in the parameters, - // use that to create the parser - __entryParser = __parserFactory.createFileEntryParser(parserKey); - __entryParserKey = parserKey; - } else { - // if no parserKey was supplied, check for a configuration - // in the params, and if it has a non-empty system type, use that. - if (null != __configuration && __configuration.getServerSystemKey().length() > 0) { - __entryParser = __parserFactory.createFileEntryParser(__configuration); - __entryParserKey = __configuration.getServerSystemKey(); - } else { - // if a parserKey hasn't been supplied, and a configuration - // hasn't been supplied, and the override property is not set - // then autodetect by calling - // the SYST command and use that to choose the parser. - String systemType = System.getProperty(FTP_SYSTEM_TYPE); - if (systemType == null) { - systemType = getSystemType(); // cannot be null - Properties override = getOverrideProperties(); - if (override != null) { - String newType = override.getProperty(systemType); - if (newType != null) { - systemType = newType; - } - } - } - if (null != __configuration) { // system type must have been empty above - __entryParser = __parserFactory.createFileEntryParser( - new FTPClientConfig(systemType, __configuration)); - } else { - __entryParser = __parserFactory.createFileEntryParser(systemType); - } - __entryParserKey = systemType; - } - } - } - } - - /** - * private method through which all listFiles() and - * initiateListParsing methods pass once a parser is determined. - * - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - * @see FTPListParseEngine - */ - private FTPListParseEngine initiateListParsing(FTPFileEntryParser parser, String pathname) - throws IOException { - Socket socket = _openDataConnection_(FTPCmd.LIST, getListArguments(pathname)); - - FTPListParseEngine engine = new FTPListParseEngine(parser, __configuration); - if (socket == null) { - return engine; - } - - try { - engine.readServerList(socket.getInputStream(), getControlEncoding()); - } finally { - Util.closeQuietly(socket); - } - - completePendingCommand(); - return engine; - } - - /** - * Initiate list parsing for MLSD listings. - * - * @return the engine - * @throws IOException - */ - private FTPListParseEngine initiateMListParsing(String pathname) throws IOException { - Socket socket = _openDataConnection_(FTPCmd.MLSD, pathname); - FTPListParseEngine engine = - new FTPListParseEngine(MLSxEntryParser.getInstance(), __configuration); - if (socket == null) { - return engine; - } - - try { - engine.readServerList(socket.getInputStream(), getControlEncoding()); - } finally { - Util.closeQuietly(socket); - completePendingCommand(); - } - return engine; - } - - /** - * @param pathname the initial pathname - * @return the adjusted string with "-a" added if necessary - * @since 2.0 - */ - protected String getListArguments(String pathname) { - if (getListHiddenFiles()) { - if (pathname != null) { - StringBuilder sb = new StringBuilder(pathname.length() + 3); - sb.append("-a "); - sb.append(pathname); - return sb.toString(); - } else { - return "-a"; - } - } - - return pathname; - } - - /** - * Issue the FTP STAT command to the server. - * - * @return The status information returned by the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String getStatus() throws IOException { - if (FTPReply.isPositiveCompletion(stat())) { - return getReplyString(); - } - return null; - } - - /** - * Issue the FTP STAT command to the server for a given pathname. This - * should produce a listing of the file or directory. - * - * @param pathname the filename - * @return The status information returned by the server. - * @throws FTPConnectionClosedException If the FTP server prematurely closes the connection as a - * result - * of the client being idle or some other reason causing the server - * to send FTP reply code 421. This exception may be caught either - * as an IOException or independently as itself. - * @throws IOException If an I/O error occurs while either sending a - * command to the server or receiving a reply from the server. - */ - public String getStatus(String pathname) throws IOException { - if (FTPReply.isPositiveCompletion(stat(pathname))) { - return getReplyString(); - } - return null; - } - - /** - * Issue the FTP MDTM command (not supported by all servers) to retrieve the last - * modification time of a file. The modification string should be in the - * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in - * GMT, but not all FTP servers honour this. - * - * @param pathname The file path to query. - * @return A string representing the last file modification time in YYYYMMDDhhmmss - * format. - * @throws IOException if an I/O error occurs. - * @since 2.0 - */ - public String getModificationTime(String pathname) throws IOException { - if (FTPReply.isPositiveCompletion(mdtm(pathname))) { - return getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space - } - return null; - } - - /** - * Issue the FTP MDTM command (not supported by all servers) to retrieve the last - * modification time of a file. The modification string should be in the - * ISO 3077 form "YYYYMMDDhhmmss(.xxx)?". The timestamp represented should also be in - * GMT, but not all FTP servers honour this. - * - * @param pathname The file path to query. - * @return A FTPFile representing the last file modification time, may be {@code null}. - * The FTPFile timestamp will be null if a parse error occurs. - * @throws IOException if an I/O error occurs. - * @since 3.4 - */ - public FTPFile mdtmFile(String pathname) throws IOException { - if (FTPReply.isPositiveCompletion(mdtm(pathname))) { - String reply = - getReplyStrings()[0].substring(4); // skip the return code (e.g. 213) and the space - FTPFile file = new FTPFile(); - file.setName(pathname); - file.setRawListing(reply); - file.setTimestamp(MLSxEntryParser.parseGMTdateTime(reply)); - return file; - } - return null; - } - - /** - * Issue the FTP MFMT command (not supported by all servers) which sets the last - * modified time of a file. - * - * The timestamp should be in the form YYYYMMDDhhmmss. It should also - * be in GMT, but not all servers honour this. - * - * An FTP server would indicate its support of this feature by including "MFMT" - * in its response to the FEAT command, which may be retrieved by FTPClient.features() - * - * @param pathname The file path for which last modified time is to be changed. - * @param timeval The timestamp to set to, in YYYYMMDDhhmmss format. - * @return true if successfully set, false if not - * @throws IOException if an I/O error occurs. - * @see http://tools.ietf.org/html/draft-somers-ftp-mfxx-04 - * @since 2.2 - */ - public boolean setModificationTime(String pathname, String timeval) throws IOException { - return (FTPReply.isPositiveCompletion(mfmt(pathname, timeval))); - } - - /** - * Set the internal buffer size for buffered data streams. - * - * @param bufSize The size of the buffer. Use a non-positive value to use the default. - */ - public void setBufferSize(int bufSize) { - __bufferSize = bufSize; - } - - /** - * Retrieve the current internal buffer size for buffered data streams. - * - * @return The current buffer size. - */ - public int getBufferSize() { - return __bufferSize; - } - - /** - * Sets the value to be used for the data socket SO_SNDBUF option. - * If the value is positive, the option will be set when the data socket has been created. - * - * @param bufSize The size of the buffer, zero or negative means the value is ignored. - * @since 3.3 - */ - public void setSendDataSocketBufferSize(int bufSize) { - __sendDataSocketBufferSize = bufSize; - } - - /** - * Retrieve the value to be used for the data socket SO_SNDBUF option. - * - * @return The current buffer size. - * @since 3.3 - */ - public int getSendDataSocketBufferSize() { - return __sendDataSocketBufferSize; - } - - /** - * Sets the value to be used for the data socket SO_RCVBUF option. - * If the value is positive, the option will be set when the data socket has been created. - * - * @param bufSize The size of the buffer, zero or negative means the value is ignored. - * @since 3.3 - */ - public void setReceieveDataSocketBufferSize(int bufSize) { - __receiveDataSocketBufferSize = bufSize; - } - - /** - * Retrieve the value to be used for the data socket SO_RCVBUF option. - * - * @return The current buffer size. - * @since 3.3 - */ - public int getReceiveDataSocketBufferSize() { - return __receiveDataSocketBufferSize; - } - - /** - * Implementation of the {@link Configurable Configurable} interface. - * In the case of this class, configuring merely makes the config object available for the - * factory methods that construct parsers. - * - * @param config {@link FTPClientConfig FTPClientConfig} object used to - * provide non-standard configurations to the parser. - * @since 1.4 - */ - @Override public void configure(FTPClientConfig config) { - this.__configuration = config; - } - - /** - * You can set this to true if you would like to get hidden files when {@link #listFiles} too. - * A LIST -a will be issued to the ftp server. - * It depends on your ftp server if you need to call this method, also dont expect to get rid - * of hidden files if you call this method with "false". - * - * @param listHiddenFiles true if hidden files should be listed - * @since 2.0 - */ - public void setListHiddenFiles(boolean listHiddenFiles) { - this.__listHiddenFiles = listHiddenFiles; - } - - /** - * @return the current state - * @see #setListHiddenFiles(boolean) - * @since 2.0 - */ - public boolean getListHiddenFiles() { - return this.__listHiddenFiles; - } - - /** - * Whether should attempt to use EPSV with IPv4. - * Default (if not set) is false - * - * @return true if should attempt EPSV - * @since 2.2 - */ - public boolean isUseEPSVwithIPv4() { - return __useEPSVwithIPv4; - } - - /** - * Set whether to use EPSV with IPv4. - * Might be worth enabling in some circumstances. - * - * For example, when using IPv4 with NAT it - * may work with some rare configurations. - * E.g. if FTP server has a static PASV address (external network) - * and the client is coming from another internal network. - * In that case the data connection after PASV command would fail, - * while EPSV would make the client succeed by taking just the port. - * - * @param selected value to set. - * @since 2.2 - */ - public void setUseEPSVwithIPv4(boolean selected) { - this.__useEPSVwithIPv4 = selected; - } - - /** - * Set the listener to be used when performing store/retrieve operations. - * The default value (if not set) is {@code null}. - * - * @param listener to be used, may be {@code null} to disable - * @since 3.0 - */ - public void setCopyStreamListener(CopyStreamListener listener) { - __copyStreamListener = listener; - } - - /** - * Obtain the currently active listener. - * - * @return the listener, may be {@code null} - * @since 3.0 - */ - public CopyStreamListener getCopyStreamListener() { - return __copyStreamListener; - } - - /** - * Set the time to wait between sending control connection keepalive messages - * when processing file upload or download. - * - * @param controlIdle the wait (in secs) between keepalive messages. Zero (or less) disables. - * @see #setControlKeepAliveReplyTimeout(int) - * @since 3.0 - */ - public void setControlKeepAliveTimeout(long controlIdle) { - __controlKeepAliveTimeout = controlIdle * 1000; - } - - /** - * Get the time to wait between sending control connection keepalive messages. - * - * @return the number of seconds between keepalive messages. - * @since 3.0 - */ - public long getControlKeepAliveTimeout() { - return __controlKeepAliveTimeout / 1000; - } - - /** - * Set how long to wait for control keep-alive message replies. - * - * @param timeout number of milliseconds to wait (defaults to 1000) - * @see #setControlKeepAliveTimeout(long) - * @since 3.0 - */ - public void setControlKeepAliveReplyTimeout(int timeout) { - __controlKeepAliveReplyTimeout = timeout; - } - - /** - * Get how long to wait for control keep-alive message replies. - * - * @return wait time in msec - * @since 3.0 - */ - public int getControlKeepAliveReplyTimeout() { - return __controlKeepAliveReplyTimeout; - } - - /** - * Enable or disable passive mode NAT workaround. - * If enabled, a site-local PASV mode reply address will be replaced with the - * remote host address to which the PASV mode request was sent - * (unless that is also a site local address). - * This gets around the problem that some NAT boxes may change the - * reply. - * - * The default is true, i.e. site-local replies are replaced. - * - * @param enabled true to enable replacing internal IP's in passive - * mode. - * @deprecated (3.6) use {@link #setPassiveNatWorkaroundStrategy(HostnameResolver)} instead - */ - @Deprecated public void setPassiveNatWorkaround(boolean enabled) { - if (enabled) { - this.__passiveNatWorkaroundStrategy = new NatServerResolverImpl(this); - } else { - this.__passiveNatWorkaroundStrategy = null; - } - } - - /** - * Set the workaround strategy to replace the PASV mode reply addresses. - * This gets around the problem that some NAT boxes may change the reply. - * - * The default implementation is {@code NatServerResolverImpl}, i.e. site-local - * replies are replaced. - * - * @param resolver strategy to replace internal IP's in passive mode - * or null to disable the workaround (i.e. use PASV mode reply address.) - * @since 3.6 - */ - public void setPassiveNatWorkaroundStrategy(HostnameResolver resolver) { - this.__passiveNatWorkaroundStrategy = resolver; - } - - /** - * Strategy interface for updating host names received from FTP server - * for passive NAT workaround. - * - * @since 3.6 - */ - public static interface HostnameResolver { - String resolve(String hostname) throws UnknownHostException; - } - - /** - * Default strategy for passive NAT workaround (site-local - * replies are replaced.) - * - * @since 3.6 - */ - public static class NatServerResolverImpl implements HostnameResolver { - private FTPClient client; - - public NatServerResolverImpl(FTPClient client) { - this.client = client; - } - - @Override public String resolve(String hostname) throws UnknownHostException { - String newHostname = hostname; - InetAddress host = InetAddress.getByName(newHostname); - // reply is a local address, but target is not - assume NAT box changed the PASV reply - if (host.isSiteLocalAddress()) { - InetAddress remote = this.client.getRemoteAddress(); - if (!remote.isSiteLocalAddress()) { - newHostname = remote.getHostAddress(); - } - } - return newHostname; - } - } - - private OutputStream getBufferedOutputStream(OutputStream outputStream) { - if (__bufferSize > 0) { - return new BufferedOutputStream(outputStream, __bufferSize); - } - return new BufferedOutputStream(outputStream); - } - - private InputStream getBufferedInputStream(InputStream inputStream) { - if (__bufferSize > 0) { - return new BufferedInputStream(inputStream, __bufferSize); - } - return new BufferedInputStream(inputStream); - } - - // @since 3.0 - private static class CSL implements CopyStreamListener { - - private final FTPClient parent; - private final long idle; - private final int currentSoTimeout; - - private long time = System.currentTimeMillis(); - private int notAcked; - private OnFtpInputStreamListener listener; - private long lTime = time; - - CSL(FTPClient parent, long idleTime, int maxWait) throws SocketException { - this(parent, idleTime, maxWait, null); - } - - CSL(FTPClient parent, long idleTime, int maxWait, OnFtpInputStreamListener listener) - throws SocketException { - this.idle = idleTime; - this.parent = parent; - this.currentSoTimeout = parent.getSoTimeout(); - parent.setSoTimeout(maxWait); - this.listener = listener; - } - - @Override public void bytesTransferred(CopyStreamEvent event) { - bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), - event.getStreamSize()); - } - - @Override public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, - long streamSize) { - long now = System.currentTimeMillis(); - if (now - time > idle) { - try { - parent.__noop(); - } catch (SocketTimeoutException e) { - notAcked++; - } catch (IOException e) { - // Ignored - } - - time = now; - } - //if (now - lTime > 100) { - if (listener != null) { - listener.onFtpInputStream(parent, totalBytesTransferred, bytesTransferred, streamSize); - } - //lTime = now; - //} - } - - void cleanUp() throws IOException { - try { - while (notAcked-- > 0) { - parent.__getReplyNoReport(); - } - } finally { - parent.setSoTimeout(currentSoTimeout); - } - } - } - - /** - * Merge two copystream listeners, either or both of which may be null. - * - * @param local the listener used by this class, may be null - * @return a merged listener or a single listener or null - * @since 3.0 - */ - private CopyStreamListener __mergeListeners(CopyStreamListener local) { - if (local == null) { - return __copyStreamListener; - } - if (__copyStreamListener == null) { - return local; - } - // Both are non-null - CopyStreamAdapter merged = new CopyStreamAdapter(); - merged.addCopyStreamListener(local); - merged.addCopyStreamListener(__copyStreamListener); - return merged; - } - - /** - * Enables or disables automatic server encoding detection (only UTF-8 supported). - *

- * Does not affect existing connections; must be invoked before a connection is established. - * - * @param autodetect If true, automatic server encoding detection will be enabled. - */ - public void setAutodetectUTF8(boolean autodetect) { - __autodetectEncoding = autodetect; - } - - /** - * Tells if automatic server encoding detection is enabled or disabled. - * - * @return true, if automatic server encoding detection is enabled. - */ - public boolean getAutodetectUTF8() { - return __autodetectEncoding; - } - - // Method for use by unit test code only - FTPFileEntryParser getEntryParser() { - return __entryParser; - } - - // DEPRECATED METHODS - for API compatibility only - DO NOT USE - - /** - * @return the name - * @throws IOException on error - * @deprecated use {@link #getSystemType()} instead - */ - @Deprecated public String getSystemName() throws IOException { - if (__systemName == null && FTPReply.isPositiveCompletion(syst())) { - __systemName = _replyLines.get(_replyLines.size() - 1).substring(4); - } - return __systemName; - } -} - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ -/* kate: indent-width 4; replace-tabs on; */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java deleted file mode 100644 index abe8902e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPClientConfig.java +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import aria.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl; -import aria.apache.commons.net.ftp.parser.FTPTimestampParserImpl; -import java.text.DateFormatSymbols; -import java.util.Collection; -import java.util.Locale; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.TreeMap; - -/** - *

- * This class implements an alternate means of configuring the - * {@link FTPClient FTPClient} object and - * also subordinate objects which it uses. Any class implementing the - * {@link Configurable Configurable } - * interface can be configured by this object. - *

- * In particular this class was designed primarily to support configuration - * of FTP servers which express file timestamps in formats and languages - * other than those for the US locale, which although it is the most common - * is not universal. Unfortunately, nothing in the FTP spec allows this to - * be determined in an automated way, so manual configuration such as this - * is necessary. - *

- * This functionality was designed to allow existing clients to work exactly - * as before without requiring use of this component. This component should - * only need to be explicitly invoked by the user of this package for problem - * cases that previous implementations could not solve. - *

- *

Examples of use of FTPClientConfig

- * Use cases: - * You are trying to access a server that - *
    - *
  • lists files with timestamps that use month names in languages other - * than English
  • - *
  • lists files with timestamps that use date formats other - * than the American English "standard" MM dd yyyy
  • - *
  • is in different timezone and you need accurate timestamps for - * dependency checking as in Ant
  • - *
- *

- * Unpaged (whole list) access on a UNIX server that uses French month names - * but uses the "standard" MMM d yyyy date formatting - *

- *    FTPClient f=FTPClient();
- *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
- *    conf.setServerLanguageCode("fr");
- *    f.configure(conf);
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = listFiles(directory);
- * 
- *

- * Paged access on a UNIX server that uses Danish month names - * and "European" date formatting in Denmark's time zone, when you - * are in some other time zone. - *

- *    FTPClient f=FTPClient();
- *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
- *    conf.setServerLanguageCode("da");
- *    conf.setDefaultDateFormat("d MMM yyyy");
- *    conf.setRecentDateFormat("d MMM HH:mm");
- *    conf.setTimeZoneId("Europe/Copenhagen");
- *    f.configure(conf);
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPListParseEngine engine =
- *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
- *
- *    while (engine.hasNext()) {
- *       FTPFile[] files = engine.getNext(25);  // "page size" you want
- *       //do whatever you want with these files, display them, etc.
- *       //expensive FTPFile objects not created until needed.
- *    }
- * 
- *

- * Unpaged (whole list) access on a VMS server that uses month names - * in a language not {@link #getSupportedLanguageCodes() supported} by the system. - * but uses the "standard" MMM d yyyy date formatting - *

- *    FTPClient f=FTPClient();
- *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
- *    conf.setShortMonthNames(
- *        "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
- *    f.configure(conf);
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = listFiles(directory);
- * 
- *

- * Unpaged (whole list) access on a Windows-NT server in a different time zone. - * (Note, since the NT Format uses numeric date formatting, language issues - * are irrelevant here). - *

- *    FTPClient f=FTPClient();
- *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
- *    conf.setTimeZoneId("America/Denver");
- *    f.configure(conf);
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = listFiles(directory);
- * 
- * Unpaged (whole list) access on a Windows-NT server in a different time zone - * but which has been configured to use a unix-style listing format. - *
- *    FTPClient f=FTPClient();
- *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
- *    conf.setTimeZoneId("America/Denver");
- *    f.configure(conf);
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = listFiles(directory);
- * 
- * - * @see Configurable - * @see FTPClient - * @see FTPTimestampParserImpl#configure(FTPClientConfig) - * @see ConfigurableFTPFileEntryParserImpl - * @since 1.4 - */ -public class FTPClientConfig { - - /** - * Identifier by which a unix-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_UNIX = "UNIX"; - - /** - * Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are - * trimmed from file names. This is to maintain backwards compatibility with - * the original behaviour of the parser which ignored multiple spaces between the date - * and the start of the file name. - * - * @since 3.4 - */ - public static final String SYST_UNIX_TRIM_LEADING = "UNIX_LTRIM"; - - /** - * Identifier by which a vms-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_VMS = "VMS"; - - /** - * Identifier by which a WindowsNT-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_NT = "WINDOWS"; - - /** - * Identifier by which an OS/2-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_OS2 = "OS/2"; - - /** - * Identifier by which an OS/400-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_OS400 = "OS/400"; - - /** - * Identifier by which an AS/400-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_AS400 = "AS/400"; - - /** - * Identifier by which an MVS-based ftp server is known throughout - * the commons-net ftp system. - */ - public static final String SYST_MVS = "MVS"; - - /** - * Some servers return an "UNKNOWN Type: L8" message - * in response to the SYST command. We set these to be a Unix-type system. - * This may happen if the ftpd in question was compiled without system - * information. - * - * NET-230 - Updated to be UPPERCASE so that the check done in - * createFileEntryParser will succeed. - * - * @since 1.5 - */ - public static final String SYST_L8 = "TYPE: L8"; - - /** - * Identifier by which an Netware-based ftp server is known throughout - * the commons-net ftp system. - * - * @since 1.5 - */ - public static final String SYST_NETWARE = "NETWARE"; - - /** - * Identifier by which a Mac pre OS-X -based ftp server is known throughout - * the commons-net ftp system. - * - * @since 3.1 - */ - // Full string is "MACOS Peter's Server"; the substring below should be enough - public static final String SYST_MACOS_PETER = "MACOS PETER"; // NET-436 - - private final String serverSystemKey; - private String defaultDateFormatStr = null; - private String recentDateFormatStr = null; - private boolean lenientFutureDates = true; // NET-407 - private String serverLanguageCode = null; - private String shortMonthNames = null; - private String serverTimeZoneId = null; - private boolean saveUnparseableEntries = false; - - /** - * The main constructor for an FTPClientConfig object - * - * @param systemKey key representing system type of the server being - * connected to. See {@link #getServerSystemKey() serverSystemKey} - * If set to the empty string, then FTPClient uses the system type returned by the server. - * However this is not recommended for general use; - * the correct system type should be set if it is known. - */ - public FTPClientConfig(String systemKey) { - this.serverSystemKey = systemKey; - } - - /** - * Convenience constructor mainly for use in testing. - * Constructs a UNIX configuration. - */ - public FTPClientConfig() { - this(SYST_UNIX); - } - - /** - * Constructor which allows setting of the format string member fields - * - * @param systemKey key representing system type of the server being - * connected to. See - * {@link #getServerSystemKey() serverSystemKey} - * @param defaultDateFormatStr See - * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} - * @param recentDateFormatStr See - * {@link #setRecentDateFormatStr(String) recentDateFormatStr} - * @since 3.6 - */ - public FTPClientConfig(String systemKey, String defaultDateFormatStr, - String recentDateFormatStr) { - this(systemKey); - this.defaultDateFormatStr = defaultDateFormatStr; - this.recentDateFormatStr = recentDateFormatStr; - } - - /** - * Constructor which allows setting of most member fields - * - * @param systemKey key representing system type of the server being - * connected to. See - * {@link #getServerSystemKey() serverSystemKey} - * @param defaultDateFormatStr See - * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} - * @param recentDateFormatStr See - * {@link #setRecentDateFormatStr(String) recentDateFormatStr} - * @param serverLanguageCode See - * {@link #setServerLanguageCode(String) serverLanguageCode} - * @param shortMonthNames See - * {@link #setShortMonthNames(String) shortMonthNames} - * @param serverTimeZoneId See - * {@link #setServerTimeZoneId(String) serverTimeZoneId} - */ - public FTPClientConfig(String systemKey, String defaultDateFormatStr, String recentDateFormatStr, - String serverLanguageCode, String shortMonthNames, String serverTimeZoneId) { - this(systemKey); - this.defaultDateFormatStr = defaultDateFormatStr; - this.recentDateFormatStr = recentDateFormatStr; - this.serverLanguageCode = serverLanguageCode; - this.shortMonthNames = shortMonthNames; - this.serverTimeZoneId = serverTimeZoneId; - } - - /** - * Constructor which allows setting of all member fields - * - * @param systemKey key representing system type of the server being - * connected to. See - * {@link #getServerSystemKey() serverSystemKey} - * @param defaultDateFormatStr See - * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} - * @param recentDateFormatStr See - * {@link #setRecentDateFormatStr(String) recentDateFormatStr} - * @param serverLanguageCode See - * {@link #setServerLanguageCode(String) serverLanguageCode} - * @param shortMonthNames See - * {@link #setShortMonthNames(String) shortMonthNames} - * @param serverTimeZoneId See - * {@link #setServerTimeZoneId(String) serverTimeZoneId} - * @param lenientFutureDates See - * {@link #setLenientFutureDates(boolean) lenientFutureDates} - * @param saveUnparseableEntries See - * {@link #setUnparseableEntries(boolean) saveUnparseableEntries} - */ - public FTPClientConfig(String systemKey, String defaultDateFormatStr, String recentDateFormatStr, - String serverLanguageCode, String shortMonthNames, String serverTimeZoneId, - boolean lenientFutureDates, boolean saveUnparseableEntries) { - this(systemKey); - this.defaultDateFormatStr = defaultDateFormatStr; - this.lenientFutureDates = lenientFutureDates; - this.recentDateFormatStr = recentDateFormatStr; - this.saveUnparseableEntries = saveUnparseableEntries; - this.serverLanguageCode = serverLanguageCode; - this.shortMonthNames = shortMonthNames; - this.serverTimeZoneId = serverTimeZoneId; - } - - // Copy constructor, intended for use by FTPClient only - FTPClientConfig(String systemKey, FTPClientConfig config) { - this.serverSystemKey = systemKey; - this.defaultDateFormatStr = config.defaultDateFormatStr; - this.lenientFutureDates = config.lenientFutureDates; - this.recentDateFormatStr = config.recentDateFormatStr; - this.saveUnparseableEntries = config.saveUnparseableEntries; - this.serverLanguageCode = config.serverLanguageCode; - this.serverTimeZoneId = config.serverTimeZoneId; - this.shortMonthNames = config.shortMonthNames; - } - - /** - * Copy constructor - * - * @param config source - * @since 3.6 - */ - public FTPClientConfig(FTPClientConfig config) { - this.serverSystemKey = config.serverSystemKey; - this.defaultDateFormatStr = config.defaultDateFormatStr; - this.lenientFutureDates = config.lenientFutureDates; - this.recentDateFormatStr = config.recentDateFormatStr; - this.saveUnparseableEntries = config.saveUnparseableEntries; - this.serverLanguageCode = config.serverLanguageCode; - this.serverTimeZoneId = config.serverTimeZoneId; - this.shortMonthNames = config.shortMonthNames; - } - - private static final Map LANGUAGE_CODE_MAP = new TreeMap(); - - static { - - // if there are other commonly used month name encodings which - // correspond to particular locales, please add them here. - - // many locales code short names for months as all three letters - // these we handle simply. - LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); - LANGUAGE_CODE_MAP.put("de", Locale.GERMAN); - LANGUAGE_CODE_MAP.put("it", Locale.ITALIAN); - LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish - LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese - LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish - LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish - LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian - LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch - LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian - LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian - LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian - LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak - LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian - - // some don't - LANGUAGE_CODE_MAP.put("fr", - "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french - } - - /** - * Getter for the serverSystemKey property. This property - * specifies the general type of server to which the client connects. - * Should be either one of the FTPClientConfig.SYST_* codes - * or else the fully qualified class name of a parser implementing both - * the FTPFileEntryParser and Configurable - * interfaces. - * - * @return Returns the serverSystemKey property. - */ - public String getServerSystemKey() { - return serverSystemKey; - } - - /** - * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} - * property. - * - * @return Returns the defaultDateFormatStr property. - */ - public String getDefaultDateFormatStr() { - return defaultDateFormatStr; - } - - /** - * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property. - * - * @return Returns the recentDateFormatStr property. - */ - - public String getRecentDateFormatStr() { - return recentDateFormatStr; - } - - /** - * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property. - * - * @return Returns the serverTimeZoneId property. - */ - public String getServerTimeZoneId() { - return serverTimeZoneId; - } - - /** - *

- * getter for the {@link #setShortMonthNames(String) shortMonthNames} - * property. - *

- * - * @return Returns the shortMonthNames. - */ - public String getShortMonthNames() { - return shortMonthNames; - } - - /** - *

- * getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. - *

- * - * @return Returns the serverLanguageCode property. - */ - public String getServerLanguageCode() { - return serverLanguageCode; - } - - /** - *

- * getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property. - *

- * - * @return Returns the lenientFutureDates. - * @since 1.5 - */ - public boolean isLenientFutureDates() { - return lenientFutureDates; - } - - /** - *

- * setter for the defaultDateFormatStr property. This property - * specifies the main date format that will be used by a parser configured - * by this configuration to parse file timestamps. If this is not - * specified, such a parser will use as a default value, the most commonly - * used format which will be in as used in en_US locales. - *

- * This should be in the format described for - * java.text.SimpleDateFormat. - * property. - *

- * - * @param defaultDateFormatStr The defaultDateFormatStr to set. - */ - public void setDefaultDateFormatStr(String defaultDateFormatStr) { - this.defaultDateFormatStr = defaultDateFormatStr; - } - - /** - *

- * setter for the recentDateFormatStr property. This property - * specifies a secondary date format that will be used by a parser - * configured by this configuration to parse file timestamps, typically - * those less than a year old. If this is not specified, such a parser - * will not attempt to parse using an alternate format. - *

- *

- * This is used primarily in unix-based systems. - *

- *

- * This should be in the format described for - * java.text.SimpleDateFormat. - *

- * - * @param recentDateFormatStr The recentDateFormatStr to set. - */ - public void setRecentDateFormatStr(String recentDateFormatStr) { - this.recentDateFormatStr = recentDateFormatStr; - } - - /** - *

- * setter for the lenientFutureDates property. This boolean property - * (default: false) only has meaning when a - * {@link #setRecentDateFormatStr(String) recentDateFormatStr} property - * has been set. In that case, if this property is set true, then the - * parser, when it encounters a listing parseable with the recent date - * format, will only consider a date to belong to the previous year if - * it is more than one day in the future. This will allow all - * out-of-synch situations (whether based on "slop" - i.e. servers simply - * out of synch with one another or because of time zone differences - - * but in the latter case it is highly recommended to use the - * {@link #setServerTimeZoneId(String) serverTimeZoneId} property - * instead) to resolve correctly. - *

- * This is used primarily in unix-based systems. - *

- * - * @param lenientFutureDates set true to compensate for out-of-synch - * conditions. - */ - public void setLenientFutureDates(boolean lenientFutureDates) { - this.lenientFutureDates = lenientFutureDates; - } - - /** - *

- * setter for the serverTimeZoneId property. This property - * allows a time zone to be specified corresponding to that known to be - * used by an FTP server in file listings. This might be particularly - * useful to clients such as Ant that try to use these timestamps for - * dependency checking. - *

- * This should be one of the identifiers used by - * java.util.TimeZone to refer to time zones, for example, - * America/Chicago or Asia/Rangoon. - *

- * - * @param serverTimeZoneId The serverTimeZoneId to set. - */ - public void setServerTimeZoneId(String serverTimeZoneId) { - this.serverTimeZoneId = serverTimeZoneId; - } - - /** - *

- * setter for the shortMonthNames property. - * This property allows the user to specify a set of month names - * used by the server that is different from those that may be - * specified using the {@link #setServerLanguageCode(String) serverLanguageCode} - * property. - *

- * This should be a string containing twelve strings each composed of - * three characters, delimited by pipe (|) characters. Currently, - * only 8-bit ASCII characters are known to be supported. For example, - * a set of month names used by a hypothetical Icelandic FTP server might - * conceivably be specified as - * "jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des". - *

- * - * @param shortMonthNames The value to set to the shortMonthNames property. - */ - public void setShortMonthNames(String shortMonthNames) { - this.shortMonthNames = shortMonthNames; - } - - /** - *

- * setter for the serverLanguageCode property. This property allows - * user to specify a - * - * two-letter ISO-639 language code that will be used to - * configure the set of month names used by the file timestamp parser. - * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} - * is specified, parsing will assume English month names, which may or - * may not be significant, depending on whether the date format(s) - * specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} - * and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using - * numeric or alphabetic month names. - *

- *

If the code supplied is not supported here, en_US - * month names will be used. We are supporting here those language - * codes which, when a java.util.Locale is constucted - * using it, and a java.text.SimpleDateFormat is - * constructed using that Locale, the array returned by the - * SimpleDateFormat's getShortMonths() method consists - * solely of three 8-bit ASCII character strings. Additionally, - * languages which do not meet this requirement are included if a - * common alternative set of short month names is known to be used. - * This means that users who can tell us of additional such encodings - * may get them added to the list of supported languages by contacting - * the Apache Commons Net team. - *

- *

- * Please note that this attribute will NOT be used to determine a - * locale-based date format for the language. - * Experience has shown that many if not most FTP servers outside the - * United States employ the standard en_US date format - * orderings of MMM d yyyy and MMM d HH:mm - * and attempting to deduce this automatically here would cause more - * problems than it would solve. The date format must be changed - * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or - * {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters. - *

- * - * @param serverLanguageCode The value to set to the serverLanguageCode property. - */ - public void setServerLanguageCode(String serverLanguageCode) { - this.serverLanguageCode = serverLanguageCode; - } - - /** - * Looks up the supplied language code in the internally maintained table of - * language codes. Returns a DateFormatSymbols object configured with - * short month names corresponding to the code. If there is no corresponding - * entry in the table, the object returned will be that for - * Locale.US - * - * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} - * @return a DateFormatSymbols object configured with short month names - * corresponding to the supplied code, or with month names for - * Locale.US if there is no corresponding entry in the internal - * table. - */ - public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) { - Object lang = LANGUAGE_CODE_MAP.get(languageCode); - if (lang != null) { - if (lang instanceof Locale) { - return new DateFormatSymbols((Locale) lang); - } else if (lang instanceof String) { - return getDateFormatSymbols((String) lang); - } - } - return new DateFormatSymbols(Locale.US); - } - - /** - * Returns a DateFormatSymbols object configured with short month names - * as in the supplied string - * - * @param shortmonths This should be as described in - * {@link #setShortMonthNames(String) shortMonthNames} - * @return a DateFormatSymbols object configured with short month names - * as in the supplied string - */ - public static DateFormatSymbols getDateFormatSymbols(String shortmonths) { - String[] months = splitShortMonthString(shortmonths); - DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); - dfs.setShortMonths(months); - return dfs; - } - - private static String[] splitShortMonthString(String shortmonths) { - StringTokenizer st = new StringTokenizer(shortmonths, "|"); - int monthcnt = st.countTokens(); - if (12 != monthcnt) { - throw new IllegalArgumentException("expecting a pipe-delimited string containing 12 tokens"); - } - String[] months = new String[13]; - int pos = 0; - while (st.hasMoreTokens()) { - months[pos++] = st.nextToken(); - } - months[pos] = ""; - return months; - } - - /** - * Returns a Collection of all the language codes currently supported - * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} - * for a functional descrption of language codes within this system. - * - * @return a Collection of all the language codes currently supported - * by this class - */ - public static Collection getSupportedLanguageCodes() { - return LANGUAGE_CODE_MAP.keySet(); - } - - /** - * Allow list parsing methods to create basic FTPFile entries if parsing fails. - *

- * In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()} - * and {@link FTPFile#isValid()} will return {@code false} - * - * @param saveUnparseable if true, then create FTPFile entries if parsing fails - * @since 3.4 - */ - public void setUnparseableEntries(boolean saveUnparseable) { - this.saveUnparseableEntries = saveUnparseable; - } - - /** - * @return true if list parsing should return FTPFile entries even for unparseable response lines - *

- * If true, the FTPFile for any unparseable entries will contain only the unparsed entry - * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false} - * @since 3.4 - */ - public boolean getUnparseableEntries() { - return this.saveUnparseableEntries; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java deleted file mode 100644 index de14835b..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCmd.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.ftp; - -/** - * @since 3.3 - */ -public enum FTPCmd { - ABOR, ACCT, ALLO, APPE, CDUP, CWD, DELE, EPRT, EPSV, FEAT, HELP, LIST, MDTM, MFMT, MKD, MLSD, MLST, MODE, NLST, NOOP, PASS, PASV, PORT, PWD, QUIT, REIN, REST, RETR, RMD, RNFR, RNTO, SITE, SMNT, STAT, STOR, STOU, STRU, SYST, TYPE, USER,; - - // Aliases - - public static final FTPCmd ABORT = ABOR; - public static final FTPCmd ACCOUNT = ACCT; - public static final FTPCmd ALLOCATE = ALLO; - public static final FTPCmd APPEND = APPE; - public static final FTPCmd CHANGE_TO_PARENT_DIRECTORY = CDUP; - public static final FTPCmd CHANGE_WORKING_DIRECTORY = CWD; - public static final FTPCmd DATA_PORT = PORT; - public static final FTPCmd DELETE = DELE; - public static final FTPCmd FEATURES = FEAT; - public static final FTPCmd FILE_STRUCTURE = STRU; - public static final FTPCmd GET_MOD_TIME = MDTM; - public static final FTPCmd LOGOUT = QUIT; - public static final FTPCmd MAKE_DIRECTORY = MKD; - public static final FTPCmd MOD_TIME = MDTM; - public static final FTPCmd NAME_LIST = NLST; - public static final FTPCmd PASSIVE = PASV; - public static final FTPCmd PASSWORD = PASS; - public static final FTPCmd PRINT_WORKING_DIRECTORY = PWD; - public static final FTPCmd REINITIALIZE = REIN; - public static final FTPCmd REMOVE_DIRECTORY = RMD; - public static final FTPCmd RENAME_FROM = RNFR; - public static final FTPCmd RENAME_TO = RNTO; - public static final FTPCmd REPRESENTATION_TYPE = TYPE; - public static final FTPCmd RESTART = REST; - public static final FTPCmd RETRIEVE = RETR; - public static final FTPCmd SET_MOD_TIME = MFMT; - public static final FTPCmd SITE_PARAMETERS = SITE; - public static final FTPCmd STATUS = STAT; - public static final FTPCmd STORE = STOR; - public static final FTPCmd STORE_UNIQUE = STOU; - public static final FTPCmd STRUCTURE_MOUNT = SMNT; - public static final FTPCmd SYSTEM = SYST; - public static final FTPCmd TRANSFER_MODE = MODE; - public static final FTPCmd USERNAME = USER; - - /** - * Retrieve the FTP protocol command string corresponding to a specified - * command code. - * - * @return The FTP protcol command string corresponding to a specified - * command code. - */ - public final String getCommand() { - return this.name(); - } - -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java deleted file mode 100644 index 3a1aef89..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPCommand.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -/** - * FTPCommand stores a set of constants for FTP command codes. To interpret - * the meaning of the codes, familiarity with RFC 959 is assumed. - * The mnemonic constant names are transcriptions from the code descriptions - * of RFC 959. For those who think in terms of the actual FTP commands, - * a set of constants such as {@link #USER USER } are provided - * where the constant name is the same as the FTP command. - * - * @deprecated use {@link FTPCmd} instead - */ -@Deprecated public final class FTPCommand { - - public static final int USER = 0; - public static final int PASS = 1; - public static final int ACCT = 2; - public static final int CWD = 3; - public static final int CDUP = 4; - public static final int SMNT = 5; - public static final int REIN = 6; - public static final int QUIT = 7; - public static final int PORT = 8; - public static final int PASV = 9; - public static final int TYPE = 10; - public static final int STRU = 11; - public static final int MODE = 12; - public static final int RETR = 13; - public static final int STOR = 14; - public static final int STOU = 15; - public static final int APPE = 16; - public static final int ALLO = 17; - public static final int REST = 18; - public static final int RNFR = 19; - public static final int RNTO = 20; - public static final int ABOR = 21; - public static final int DELE = 22; - public static final int RMD = 23; - public static final int MKD = 24; - public static final int PWD = 25; - public static final int LIST = 26; - public static final int NLST = 27; - public static final int SITE = 28; - public static final int SYST = 29; - public static final int STAT = 30; - public static final int HELP = 31; - public static final int NOOP = 32; - /** @since 2.0 */ - public static final int MDTM = 33; - /** @since 2.2 */ - public static final int FEAT = 34; - /** @since 2.2 */ - public static final int MFMT = 35; - /** @since 2.2 */ - public static final int EPSV = 36; - /** @since 2.2 */ - public static final int EPRT = 37; - - /** - * Machine parseable list for a directory - * - * @since 3.0 - */ - public static final int MLSD = 38; - - /** - * Machine parseable list for a single file - * - * @since 3.0 - */ - public static final int MLST = 39; - - // Must agree with final entry above; used to check array size - private static final int LAST = MLST; - - public static final int USERNAME = USER; - public static final int PASSWORD = PASS; - public static final int ACCOUNT = ACCT; - public static final int CHANGE_WORKING_DIRECTORY = CWD; - public static final int CHANGE_TO_PARENT_DIRECTORY = CDUP; - public static final int STRUCTURE_MOUNT = SMNT; - public static final int REINITIALIZE = REIN; - public static final int LOGOUT = QUIT; - public static final int DATA_PORT = PORT; - public static final int PASSIVE = PASV; - public static final int REPRESENTATION_TYPE = TYPE; - public static final int FILE_STRUCTURE = STRU; - public static final int TRANSFER_MODE = MODE; - public static final int RETRIEVE = RETR; - public static final int STORE = STOR; - public static final int STORE_UNIQUE = STOU; - public static final int APPEND = APPE; - public static final int ALLOCATE = ALLO; - public static final int RESTART = REST; - public static final int RENAME_FROM = RNFR; - public static final int RENAME_TO = RNTO; - public static final int ABORT = ABOR; - public static final int DELETE = DELE; - public static final int REMOVE_DIRECTORY = RMD; - public static final int MAKE_DIRECTORY = MKD; - public static final int PRINT_WORKING_DIRECTORY = PWD; - // public static final int LIST = LIST; - public static final int NAME_LIST = NLST; - public static final int SITE_PARAMETERS = SITE; - public static final int SYSTEM = SYST; - public static final int STATUS = STAT; - //public static final int HELP = HELP; - //public static final int NOOP = NOOP; - - /** @since 2.0 */ - public static final int MOD_TIME = MDTM; - - /** @since 2.2 */ - public static final int FEATURES = FEAT; - /** @since 2.2 */ - public static final int GET_MOD_TIME = MDTM; - /** @since 2.2 */ - public static final int SET_MOD_TIME = MFMT; - - // Cannot be instantiated - private FTPCommand() { - } - - private static final String[] _commands = { - "USER", "PASS", "ACCT", "CWD", "CDUP", "SMNT", "REIN", "QUIT", "PORT", "PASV", "TYPE", "STRU", - "MODE", "RETR", "STOR", "STOU", "APPE", "ALLO", "REST", "RNFR", "RNTO", "ABOR", "DELE", "RMD", - "MKD", "PWD", "LIST", "NLST", "SITE", "SYST", "STAT", "HELP", "NOOP", "MDTM", "FEAT", "MFMT", - "EPSV", "EPRT", "MLSD", "MLST" - }; - - // default access needed for Unit test - static void checkArray() { - int expectedLength = LAST + 1; - if (_commands.length != expectedLength) { - throw new RuntimeException("Incorrect _commands array. Should have length " - + expectedLength - + " found " - + _commands.length); - } - } - - /** - * Retrieve the FTP protocol command string corresponding to a specified - * command code. - * - * @param command The command code. - * @return The FTP protcol command string corresponding to a specified - * command code. - */ - public static final String getCommand(int command) { - return _commands[command]; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java deleted file mode 100644 index aa95277e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPConnectionClosedException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.IOException; - -/*** - * FTPConnectionClosedException is used to indicate the premature or - * unexpected closing of an FTP connection resulting from a - * {@link FTPReply#SERVICE_NOT_AVAILABLE FTPReply.SERVICE_NOT_AVAILABLE } - * response (FTP reply code 421) to a - * failed FTP command. This exception is derived from IOException and - * therefore may be caught either as an IOException or specifically as an - * FTPConnectionClosedException. - * - * @see FTP - * @see FTPClient - ***/ - -public class FTPConnectionClosedException extends IOException { - - private static final long serialVersionUID = 3500547241659379952L; - - /*** Constructs a FTPConnectionClosedException with no message ***/ - public FTPConnectionClosedException() { - super(); - } - - /*** - * Constructs a FTPConnectionClosedException with a specified message. - * - * @param message The message explaining the reason for the exception. - ***/ - public FTPConnectionClosedException(String message) { - super(message); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFile.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFile.java deleted file mode 100644 index 5edcc119..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFile.java +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.Serializable; -import java.util.Calendar; -import java.util.Date; -import java.util.Formatter; -import java.util.TimeZone; - -/*** - * The FTPFile class is used to represent information about files stored - * on an FTP server. - * - * @see FTPFileEntryParser - * @see FTPClient#listFiles - ***/ - -public class FTPFile implements Serializable { - private static final long serialVersionUID = 9010790363003271996L; - - /** A constant indicating an FTPFile is a file. ***/ - public static final int FILE_TYPE = 0; - /** A constant indicating an FTPFile is a directory. ***/ - public static final int DIRECTORY_TYPE = 1; - /** A constant indicating an FTPFile is a symbolic link. ***/ - public static final int SYMBOLIC_LINK_TYPE = 2; - /** A constant indicating an FTPFile is of unknown type. ***/ - public static final int UNKNOWN_TYPE = 3; - - /** A constant indicating user access permissions. ***/ - public static final int USER_ACCESS = 0; - /** A constant indicating group access permissions. ***/ - public static final int GROUP_ACCESS = 1; - /** A constant indicating world access permissions. ***/ - public static final int WORLD_ACCESS = 2; - - /** A constant indicating file/directory read permission. ***/ - public static final int READ_PERMISSION = 0; - /** A constant indicating file/directory write permission. ***/ - public static final int WRITE_PERMISSION = 1; - /** - * A constant indicating file execute permission or directory listing - * permission. - ***/ - public static final int EXECUTE_PERMISSION = 2; - - private int _type, _hardLinkCount; - private long _size; - private String _rawListing, _user, _group, _name, _link; - private Calendar _date; - // If this is null, then list entry parsing failed - private final boolean[] _permissions[]; // e.g. _permissions[USER_ACCESS][READ_PERMISSION] - - /*** Creates an empty FTPFile. ***/ - public FTPFile() { - _permissions = new boolean[3][3]; - _type = UNKNOWN_TYPE; - // init these to values that do not occur in listings - // so can distinguish which fields are unset - _hardLinkCount = 0; // 0 is invalid as a link count - _size = -1; // 0 is valid, so use -1 - _user = ""; - _group = ""; - _date = null; - _name = null; - } - - /** - * Constructor for use by {@link FTPListParseEngine} only. - * Used to create FTPFile entries for failed parses - * - * @param rawListing line that could not be parsed. - * @since 3.4 - */ - FTPFile(String rawListing) { - _permissions = null; // flag that entry is invalid - _rawListing = rawListing; - _type = UNKNOWN_TYPE; - // init these to values that do not occur in listings - // so can distinguish which fields are unset - _hardLinkCount = 0; // 0 is invalid as a link count - _size = -1; // 0 is valid, so use -1 - _user = ""; - _group = ""; - _date = null; - _name = null; - } - - /*** - * Set the original FTP server raw listing from which the FTPFile was - * created. - * - * @param rawListing The raw FTP server listing. - ***/ - public void setRawListing(String rawListing) { - _rawListing = rawListing; - } - - /*** - * Get the original FTP server raw listing used to initialize the FTPFile. - * - * @return The original FTP server raw listing used to initialize the - * FTPFile. - ***/ - public String getRawListing() { - return _rawListing; - } - - /*** - * Determine if the file is a directory. - * - * @return True if the file is of type DIRECTORY_TYPE, false if - * not. - ***/ - public boolean isDirectory() { - return (_type == DIRECTORY_TYPE); - } - - /*** - * Determine if the file is a regular file. - * - * @return True if the file is of type FILE_TYPE, false if - * not. - ***/ - public boolean isFile() { - return (_type == FILE_TYPE); - } - - /*** - * Determine if the file is a symbolic link. - * - * @return True if the file is of type UNKNOWN_TYPE, false if - * not. - ***/ - public boolean isSymbolicLink() { - return (_type == SYMBOLIC_LINK_TYPE); - } - - /*** - * Determine if the type of the file is unknown. - * - * @return True if the file is of type UNKNOWN_TYPE, false if - * not. - ***/ - public boolean isUnknown() { - return (_type == UNKNOWN_TYPE); - } - - /** - * Used to indicate whether an entry is valid or not. - * If the entry is invalid, only the {@link #getRawListing()} method will be useful. - * Other methods may fail. - * - * Used in conjunction with list parsing that preseverves entries that failed to parse. - * - * @return true if the entry is valid - * @see FTPClientConfig#setUnparseableEntries(boolean) - * @since 3.4 - */ - public boolean isValid() { - return (_permissions != null); - } - - /*** - * Set the type of the file (DIRECTORY_TYPE, - * FILE_TYPE, etc.). - * - * @param type The integer code representing the type of the file. - ***/ - public void setType(int type) { - _type = type; - } - - /*** - * Return the type of the file (one of the _TYPE constants), - * e.g., if it is a directory, a regular file, or a symbolic link. - * - * @return The type of the file. - ***/ - public int getType() { - return _type; - } - - /*** - * Set the name of the file. - * - * @param name The name of the file. - ***/ - public void setName(String name) { - _name = name; - } - - /*** - * Return the name of the file. - * - * @return The name of the file. - ***/ - public String getName() { - return _name; - } - - /** - * Set the file size in bytes. - * - * @param size The file size in bytes. - */ - public void setSize(long size) { - _size = size; - } - - /*** - * Return the file size in bytes. - * - * @return The file size in bytes. - ***/ - public long getSize() { - return _size; - } - - /*** - * Set the number of hard links to this file. This is not to be - * confused with symbolic links. - * - * @param links The number of hard links to this file. - ***/ - public void setHardLinkCount(int links) { - _hardLinkCount = links; - } - - /*** - * Return the number of hard links to this file. This is not to be - * confused with symbolic links. - * - * @return The number of hard links to this file. - ***/ - public int getHardLinkCount() { - return _hardLinkCount; - } - - /*** - * Set the name of the group owning the file. This may be - * a string representation of the group number. - * - * @param group The name of the group owning the file. - ***/ - public void setGroup(String group) { - _group = group; - } - - /*** - * Returns the name of the group owning the file. Sometimes this will be - * a string representation of the group number. - * - * @return The name of the group owning the file. - ***/ - public String getGroup() { - return _group; - } - - /*** - * Set the name of the user owning the file. This may be - * a string representation of the user number; - * - * @param user The name of the user owning the file. - ***/ - public void setUser(String user) { - _user = user; - } - - /*** - * Returns the name of the user owning the file. Sometimes this will be - * a string representation of the user number. - * - * @return The name of the user owning the file. - ***/ - public String getUser() { - return _user; - } - - /*** - * If the FTPFile is a symbolic link, use this method to set the name of the - * file being pointed to by the symbolic link. - * - * @param link The file pointed to by the symbolic link. - ***/ - public void setLink(String link) { - _link = link; - } - - /*** - * If the FTPFile is a symbolic link, this method returns the name of the - * file being pointed to by the symbolic link. Otherwise it returns null. - * - * @return The file pointed to by the symbolic link (null if the FTPFile - * is not a symbolic link). - ***/ - public String getLink() { - return _link; - } - - /*** - * Set the file timestamp. This usually the last modification time. - * The parameter is not cloned, so do not alter its value after calling - * this method. - * - * @param date A Calendar instance representing the file timestamp. - ***/ - public void setTimestamp(Calendar date) { - _date = date; - } - - /*** - * Returns the file timestamp. This usually the last modification time. - * - * @return A Calendar instance representing the file timestamp. - ***/ - public Calendar getTimestamp() { - return _date; - } - - /*** - * Set if the given access group (one of the _ACCESS - * constants) has the given access permission (one of the - * _PERMISSION constants) to the file. - * - * @param access The access group (one of the _ACCESS - * constants) - * @param permission The access permission (one of the - * _PERMISSION constants) - * @param value True if permission is allowed, false if not. - * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range - ***/ - public void setPermission(int access, int permission, boolean value) { - _permissions[access][permission] = value; - } - - /*** - * Determines if the given access group (one of the _ACCESS - * constants) has the given access permission (one of the - * _PERMISSION constants) to the file. - * - * @param access The access group (one of the _ACCESS - * constants) - * @param permission The access permission (one of the - * _PERMISSION constants) - * @throws ArrayIndexOutOfBoundsException if either of the parameters is out of range - * @return true if {@link #isValid()} is {@code true &&} the associated permission is set; - * {@code false} otherwise. - ***/ - public boolean hasPermission(int access, int permission) { - if (_permissions == null) { - return false; - } - return _permissions[access][permission]; - } - - /*** - * Returns a string representation of the FTPFile information. - * - * @return A string representation of the FTPFile information. - */ - @Override public String toString() { - return getRawListing(); - } - - /*** - * Returns a string representation of the FTPFile information. - * This currently mimics the Unix listing format. - * This method uses the timezone of the Calendar entry, which is - * the server time zone (if one was provided) otherwise it is - * the local time zone. - *

- * Note: if the instance is not valid {@link #isValid()}, no useful - * information can be returned. In this case, use {@link #getRawListing()} - * instead. - * - * @return A string representation of the FTPFile information. - * @since 3.0 - */ - public String toFormattedString() { - return toFormattedString(null); - } - - /** - * Returns a string representation of the FTPFile information. - * This currently mimics the Unix listing format. - * This method allows the Calendar time zone to be overridden. - *

- * Note: if the instance is not valid {@link #isValid()}, no useful - * information can be returned. In this case, use {@link #getRawListing()} - * instead. - * - * @param timezone the timezone to use for displaying the time stamp - * If {@code null}, then use the Calendar entry timezone - * @return A string representation of the FTPFile information. - * @since 3.4 - */ - public String toFormattedString(final String timezone) { - - if (!isValid()) { - return "[Invalid: could not parse file entry]"; - } - StringBuilder sb = new StringBuilder(); - Formatter fmt = new Formatter(sb); - sb.append(formatType()); - sb.append(permissionToString(USER_ACCESS)); - sb.append(permissionToString(GROUP_ACCESS)); - sb.append(permissionToString(WORLD_ACCESS)); - fmt.format(" %4d", Integer.valueOf(getHardLinkCount())); - fmt.format(" %-8s %-8s", getUser(), getGroup()); - fmt.format(" %8d", Long.valueOf(getSize())); - Calendar timestamp = getTimestamp(); - if (timestamp != null) { - if (timezone != null) { - TimeZone newZone = TimeZone.getTimeZone(timezone); - if (!newZone.equals(timestamp.getTimeZone())) { - Date original = timestamp.getTime(); - Calendar newStamp = Calendar.getInstance(newZone); - newStamp.setTime(original); - timestamp = newStamp; - } - } - fmt.format(" %1$tY-%1$tm-%1$td", timestamp); - // Only display time units if they are present - if (timestamp.isSet(Calendar.HOUR_OF_DAY)) { - fmt.format(" %1$tH", timestamp); - if (timestamp.isSet(Calendar.MINUTE)) { - fmt.format(":%1$tM", timestamp); - if (timestamp.isSet(Calendar.SECOND)) { - fmt.format(":%1$tS", timestamp); - if (timestamp.isSet(Calendar.MILLISECOND)) { - fmt.format(".%1$tL", timestamp); - } - } - } - fmt.format(" %1$tZ", timestamp); - } - } - sb.append(' '); - sb.append(getName()); - fmt.close(); - return sb.toString(); - } - - private char formatType() { - switch (_type) { - case FILE_TYPE: - return '-'; - case DIRECTORY_TYPE: - return 'd'; - case SYMBOLIC_LINK_TYPE: - return 'l'; - default: - return '?'; - } - } - - private String permissionToString(int access) { - StringBuilder sb = new StringBuilder(); - if (hasPermission(access, READ_PERMISSION)) { - sb.append('r'); - } else { - sb.append('-'); - } - if (hasPermission(access, WRITE_PERMISSION)) { - sb.append('w'); - } else { - sb.append('-'); - } - if (hasPermission(access, EXECUTE_PERMISSION)) { - sb.append('x'); - } else { - sb.append('-'); - } - return sb.toString(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java deleted file mode 100644 index 0c04b265..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParser.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.List; - -/** - * FTPFileEntryParser defines the interface for parsing a single FTP file - * listing and converting that information into an - * {@link FTPFile} instance. - * Sometimes you will want to parse unusual listing formats, in which - * case you would create your own implementation of FTPFileEntryParser and - * if necessary, subclass FTPFile. - *

- * Here are some examples showing how to use one of the classes that - * implement this interface. - *

- * - * The first example uses the FTPClient.listFiles() - * API to pull the whole list from the subfolder subfolder in - * one call, attempting to automatically detect the parser type. This - * method, without a parserKey parameter, indicates that autodection should - * be used. - * - *

- *    FTPClient f=FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = f.listFiles("subfolder");
- * 
- * - * The second example uses the FTPClient.listFiles() - * API to pull the whole list from the current working directory in one call, - * but specifying by classname the parser to be used. For this particular - * parser class, this approach is necessary since there is no way to - * autodetect this server type. - * - *
- *    FTPClient f=FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = f.listFiles(
- *      "org.apache.commons.net.ftp.parser.EnterpriseUnixFTPFileEntryParser",
- *      ".");
- * 
- * - * The third example uses the FTPClient.listFiles() - * API to pull a single file listing in an arbitrary directory in one call, - * specifying by KEY the parser to be used, in this case, VMS. - * - *
- *    FTPClient f=FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPFile[] files = f.listFiles("VMS", "subfolder/foo.java");
- * 
- * - * For an alternative approach, see the {@link FTPListParseEngine} class - * which provides iterative access. - * - * @version $Id: FTPFileEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ - * @see FTPFile - * @see FTPClient#listFiles() - */ -public interface FTPFileEntryParser { - /** - * Parses a line of an FTP server file listing and converts it into a usable - * format in the form of an FTPFile instance. If the - * file listing line doesn't describe a file, null should be - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - * - * @param listEntry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - FTPFile parseFTPEntry(String listEntry); - - /** - * Reads the next entry using the supplied BufferedReader object up to - * whatever delemits one entry from the next. Implementors must define - * this for the particular ftp system being parsed. In many but not all - * cases, this can be defined simply by calling BufferedReader.readLine(). - * - * @param reader The BufferedReader object from which entries are to be - * read. - * @return A string representing the next ftp entry or null if none found. - * @throws IOException thrown on any IO Error reading from the reader. - */ - String readNextEntry(BufferedReader reader) throws IOException; - - /** - * This method is a hook for those implementors (such as - * VMSVersioningFTPEntryParser, and possibly others) which need to - * perform some action upon the FTPFileList after it has been created - * from the server stream, but before any clients see the list. - * - * The default implementation can be a no-op. - * - * @param original Original list after it has been created from the server stream - * @return Original list as processed by this method. - */ - List preParse(List original); -} - - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java deleted file mode 100644 index 5af13643..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileEntryParserImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.List; - -/** - * This abstract class implements both the older FTPFileListParser and - * newer FTPFileEntryParser interfaces with default functionality. - * All the classes in the parser subpackage inherit from this. - */ -public abstract class FTPFileEntryParserImpl implements FTPFileEntryParser { - /** - * The constructor for a FTPFileEntryParserImpl object. - */ - public FTPFileEntryParserImpl() { - } - - /** - * Reads the next entry using the supplied BufferedReader object up to - * whatever delimits one entry from the next. This default implementation - * simply calls BufferedReader.readLine(). - * - * @param reader The BufferedReader object from which entries are to be - * read. - * @return A string representing the next ftp entry or null if none found. - * @throws IOException thrown on any IO Error reading from the reader. - */ - @Override public String readNextEntry(BufferedReader reader) throws IOException { - return reader.readLine(); - } - - /** - * This method is a hook for those implementors (such as - * VMSVersioningFTPEntryParser, and possibly others) which need to - * perform some action upon the FTPFileList after it has been created - * from the server stream, but before any clients see the list. - * - * This default implementation does nothing. - * - * @param original Original list after it has been created from the server stream - * @return original unmodified. - */ - @Override public List preParse(List original) { - return original; - } -} - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java deleted file mode 100644 index 898ee154..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.ftp; - -/** - * Perform filtering on FTPFile entries. - * - * @since 2.2 - */ -public interface FTPFileFilter { - /** - * Checks if an FTPFile entry should be included or not. - * - * @param file entry to be checked for inclusion. May be null. - * @return true if the file is to be included, false otherwise - */ - public boolean accept(FTPFile file); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java deleted file mode 100644 index 40a59a99..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPFileFilters.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.ftp; - -/** - * Implements some simple FTPFileFilter classes. - * - * @since 2.2 - */ -public class FTPFileFilters { - - /** - * Accepts all FTPFile entries, including null. - */ - public static final FTPFileFilter ALL = new FTPFileFilter() { - @Override public boolean accept(FTPFile file) { - return true; - } - }; - - /** - * Accepts all non-null FTPFile entries. - */ - public static final FTPFileFilter NON_NULL = new FTPFileFilter() { - @Override public boolean accept(FTPFile file) { - return file != null; - } - }; - - /** - * Accepts all (non-null) FTPFile directory entries. - */ - public static final FTPFileFilter DIRECTORIES = new FTPFileFilter() { - @Override public boolean accept(FTPFile file) { - return file != null && file.isDirectory(); - } - }; -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java deleted file mode 100644 index 0304e427..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPHTTPClient.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.io.UnsupportedEncodingException; -import java.net.Inet6Address; -import java.net.Socket; -import java.net.SocketException; -import java.util.ArrayList; -import java.util.List; - -import aria.apache.commons.net.util.Base64; - -/** - * Experimental attempt at FTP client that tunnels over an HTTP proxy connection. - * - * @since 2.2 - */ -public class FTPHTTPClient extends FTPClient { - private final String proxyHost; - private final int proxyPort; - private final String proxyUsername; - private final String proxyPassword; - - private static final byte[] CRLF = { '\r', '\n' }; - private final Base64 base64 = new Base64(); - - private String tunnelHost; // Save the host when setting up a tunnel (needed for EPSV) - - public FTPHTTPClient(String proxyHost, int proxyPort, String proxyUser, String proxyPass) { - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.proxyUsername = proxyUser; - this.proxyPassword = proxyPass; - this.tunnelHost = null; - } - - public FTPHTTPClient(String proxyHost, int proxyPort) { - this(proxyHost, proxyPort, null, null); - } - - /** - * {@inheritDoc} - * - * @throws IllegalStateException if connection mode is not passive - * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead - */ - // Kept to maintain binary compatibility - // Not strictly necessary, but Clirr complains even though there is a super-impl - @Override @Deprecated protected Socket _openDataConnection_(int command, String arg) - throws IOException { - return super._openDataConnection_(command, arg); - } - - /** - * {@inheritDoc} - * - * @throws IllegalStateException if connection mode is not passive - * @since 3.1 - */ - @Override protected Socket _openDataConnection_(String command, String arg) throws IOException { - //Force local passive mode, active mode not supported by through proxy - if (getDataConnectionMode() != PASSIVE_LOCAL_DATA_CONNECTION_MODE) { - throw new IllegalStateException("Only passive connection mode supported"); - } - - final boolean isInet6Address = getRemoteAddress() instanceof Inet6Address; - String passiveHost = null; - - boolean attemptEPSV = isUseEPSVwithIPv4() || isInet6Address; - if (attemptEPSV && epsv() == FTPReply.ENTERING_EPSV_MODE) { - _parseExtendedPassiveModeReply(_replyLines.get(0)); - passiveHost = this.tunnelHost; - } else { - if (isInet6Address) { - return null; // Must use EPSV for IPV6 - } - // If EPSV failed on IPV4, revert to PASV - if (pasv() != FTPReply.ENTERING_PASSIVE_MODE) { - return null; - } - _parsePassiveModeReply(_replyLines.get(0)); - passiveHost = this.getPassiveHost(); - } - - Socket socket = _socketFactory_.createSocket(proxyHost, proxyPort); - InputStream is = socket.getInputStream(); - OutputStream os = socket.getOutputStream(); - tunnelHandshake(passiveHost, this.getPassivePort(), is, os); - if ((getRestartOffset() > 0) && !restart(getRestartOffset())) { - socket.close(); - return null; - } - - if (!FTPReply.isPositivePreliminary(sendCommand(command, arg))) { - socket.close(); - return null; - } - - return socket; - } - - @Override public void connect(String host, int port) throws SocketException, IOException { - - _socket_ = _socketFactory_.createSocket(proxyHost, proxyPort); - _input_ = _socket_.getInputStream(); - _output_ = _socket_.getOutputStream(); - Reader socketIsReader; - try { - socketIsReader = tunnelHandshake(host, port, _input_, _output_); - } catch (Exception e) { - IOException ioe = new IOException("Could not connect to " + host + " using port " + port); - ioe.initCause(e); - throw ioe; - } - super._connectAction_(socketIsReader); - } - - private BufferedReader tunnelHandshake(String host, int port, InputStream input, - OutputStream output) throws IOException, UnsupportedEncodingException { - final String connectString = "CONNECT " + host + ":" + port + " HTTP/1.1"; - final String hostString = "Host: " + host + ":" + port; - - this.tunnelHost = host; - output.write(connectString.getBytes("UTF-8")); // TODO what is the correct encoding? - output.write(CRLF); - output.write(hostString.getBytes("UTF-8")); - output.write(CRLF); - - if (proxyUsername != null && proxyPassword != null) { - final String auth = proxyUsername + ":" + proxyPassword; - final String header = - "Proxy-Authorization: Basic " + base64.encodeToString(auth.getBytes("UTF-8")); - output.write(header.getBytes("UTF-8")); - } - output.write(CRLF); - - List response = new ArrayList(); - BufferedReader reader = new BufferedReader(new InputStreamReader(input, getCharset())); - - for (String line = reader.readLine(); line != null && line.length() > 0; - line = reader.readLine()) { - response.add(line); - } - - int size = response.size(); - if (size == 0) { - throw new IOException("No response from proxy"); - } - - String code = null; - String resp = response.get(0); - if (resp.startsWith("HTTP/") && resp.length() >= 12) { - code = resp.substring(9, 12); - } else { - throw new IOException("Invalid response from proxy: " + resp); - } - - if (!"200".equals(code)) { - StringBuilder msg = new StringBuilder(); - msg.append("HTTPTunnelConnector: connection failed\r\n"); - msg.append("Response received from the proxy:\r\n"); - for (String line : response) { - msg.append(line); - msg.append("\r\n"); - } - throw new IOException(msg.toString()); - } - return reader; - } -} - - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java deleted file mode 100644 index 51e117e5..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPListParseEngine.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; - -import aria.apache.commons.net.util.Charsets; - -/** - * This class handles the entire process of parsing a listing of - * file entries from the server. - *

- * This object defines a two-part parsing mechanism. - *

- * The first part is comprised of reading the raw input into an internal - * list of strings. Every item in this list corresponds to an actual - * file. All extraneous matter emitted by the server will have been - * removed by the end of this phase. This is accomplished in conjunction - * with the FTPFileEntryParser associated with this engine, by calling - * its methods readNextEntry() - which handles the issue of - * what delimits one entry from another, usually but not always a line - * feed and preParse() - which handles removal of - * extraneous matter such as the preliminary lines of a listing, removal - * of duplicates on versioning systems, etc. - *

- * The second part is composed of the actual parsing, again in conjunction - * with the particular parser used by this engine. This is controlled - * by an iterator over the internal list of strings. This may be done - * either in block mode, by calling the getNext() and - * getPrevious() methods to provide "paged" output of less - * than the whole list at one time, or by calling the - * getFiles() method to return the entire list. - *

- * Examples: - *

- * Paged access: - *

- *    FTPClient f=FTPClient();
- *    f.connect(server);
- *    f.login(username, password);
- *    FTPListParseEngine engine = f.initiateListParsing(directory);
- *
- *    while (engine.hasNext()) {
- *       FTPFile[] files = engine.getNext(25);  // "page size" you want
- *       //do whatever you want with these files, display them, etc.
- *       //expensive FTPFile objects not created until needed.
- *    }
- * 
- *

- * For unpaged access, simply use FTPClient.listFiles(). That method - * uses this class transparently. - * - * @version $Id: FTPListParseEngine.java 1747119 2016-06-07 02:22:24Z ggregory $ - */ -public class FTPListParseEngine { - private List entries = new LinkedList(); - private ListIterator _internalIterator = entries.listIterator(); - - private final FTPFileEntryParser parser; - // Should invalid files (parse failures) be allowed? - private final boolean saveUnparseableEntries; - - public FTPListParseEngine(FTPFileEntryParser parser) { - this(parser, null); - } - - /** - * Intended for use by FTPClient only - * - * @since 3.4 - */ - FTPListParseEngine(FTPFileEntryParser parser, FTPClientConfig configuration) { - this.parser = parser; - if (configuration != null) { - this.saveUnparseableEntries = configuration.getUnparseableEntries(); - } else { - this.saveUnparseableEntries = false; - } - } - - /** - * handle the initial reading and preparsing of the list returned by - * the server. After this method has completed, this object will contain - * a list of unparsed entries (Strings) each referring to a unique file - * on the server. - * - * @param stream input stream provided by the server socket. - * @param encoding the encoding to be used for reading the stream - * @throws IOException thrown on any failure to read from the sever. - */ - public void readServerList(InputStream stream, String encoding) throws IOException { - this.entries = new LinkedList(); - readStream(stream, encoding); - this.parser.preParse(this.entries); - resetIterator(); - } - - /** - * Internal method for reading the input into the entries list. - * After this method has completed, entries will contain a - * collection of entries (as defined by - * FTPFileEntryParser.readNextEntry()), but this may contain - * various non-entry preliminary lines from the server output, duplicates, - * and other data that will not be part of the final listing. - * - * @param stream The socket stream on which the input will be read. - * @param encoding The encoding to use. - * @throws IOException thrown on any failure to read the stream - */ - private void readStream(InputStream stream, String encoding) throws IOException { - BufferedReader reader = - new BufferedReader(new InputStreamReader(stream, Charsets.toCharset(encoding))); - - String line = this.parser.readNextEntry(reader); - - while (line != null) { - this.entries.add(line); - line = this.parser.readNextEntry(reader); - } - reader.close(); - } - - /** - * Returns an array of at most quantityRequested FTPFile - * objects starting at this object's internal iterator's current position. - * If fewer than quantityRequested such - * elements are available, the returned array will have a length equal - * to the number of entries at and after after the current position. - * If no such entries are found, this array will have a length of 0. - * - * After this method is called this object's internal iterator is advanced - * by a number of positions equal to the size of the array returned. - * - * @param quantityRequested the maximum number of entries we want to get. - * @return an array of at most quantityRequested FTPFile - * objects starting at the current position of this iterator within its - * list and at least the number of elements which exist in the list at - * and after its current position. - *

- * NOTE: This array may contain null members if any of the - * individual file listings failed to parse. The caller should - * check each entry for null before referencing it. - */ - public FTPFile[] getNext(int quantityRequested) { - List tmpResults = new LinkedList(); - int count = quantityRequested; - while (count > 0 && this._internalIterator.hasNext()) { - String entry = this._internalIterator.next(); - FTPFile temp = this.parser.parseFTPEntry(entry); - if (temp == null && saveUnparseableEntries) { - temp = new FTPFile(entry); - } - tmpResults.add(temp); - count--; - } - return tmpResults.toArray(new FTPFile[tmpResults.size()]); - } - - /** - * Returns an array of at most quantityRequested FTPFile - * objects starting at this object's internal iterator's current position, - * and working back toward the beginning. - * - * If fewer than quantityRequested such - * elements are available, the returned array will have a length equal - * to the number of entries at and after after the current position. - * If no such entries are found, this array will have a length of 0. - * - * After this method is called this object's internal iterator is moved - * back by a number of positions equal to the size of the array returned. - * - * @param quantityRequested the maximum number of entries we want to get. - * @return an array of at most quantityRequested FTPFile - * objects starting at the current position of this iterator within its - * list and at least the number of elements which exist in the list at - * and after its current position. This array will be in the same order - * as the underlying list (not reversed). - *

- * NOTE: This array may contain null members if any of the - * individual file listings failed to parse. The caller should - * check each entry for null before referencing it. - */ - public FTPFile[] getPrevious(int quantityRequested) { - List tmpResults = new LinkedList(); - int count = quantityRequested; - while (count > 0 && this._internalIterator.hasPrevious()) { - String entry = this._internalIterator.previous(); - FTPFile temp = this.parser.parseFTPEntry(entry); - if (temp == null && saveUnparseableEntries) { - temp = new FTPFile(entry); - } - tmpResults.add(0, temp); - count--; - } - return tmpResults.toArray(new FTPFile[tmpResults.size()]); - } - - /** - * Returns an array of FTPFile objects containing the whole list of - * files returned by the server as read by this object's parser. - * - * @return an array of FTPFile objects containing the whole list of - * files returned by the server as read by this object's parser. - * None of the entries will be null - * @throws IOException - not ever thrown, may be removed in a later release - */ - public FTPFile[] getFiles() throws IOException // TODO remove; not actually thrown - { - return getFiles(FTPFileFilters.NON_NULL); - } - - /** - * Returns an array of FTPFile objects containing the whole list of - * files returned by the server as read by this object's parser. - * The files are filtered before being added to the array. - * - * @param filter FTPFileFilter, must not be null. - * @return an array of FTPFile objects containing the whole list of - * files returned by the server as read by this object's parser. - *

- * NOTE: This array may contain null members if any of the - * individual file listings failed to parse. The caller should - * check each entry for null before referencing it, or use the - * a filter such as {@link FTPFileFilters#NON_NULL} which does not - * allow null entries. - * @throws IOException - not ever thrown, may be removed in a later release - * @since 2.2 - */ - public FTPFile[] getFiles(FTPFileFilter filter) - throws IOException // TODO remove; not actually thrown - { - List tmpResults = new ArrayList(); - Iterator iter = this.entries.iterator(); - while (iter.hasNext()) { - String entry = iter.next(); - FTPFile temp = this.parser.parseFTPEntry(entry); - if (temp == null && saveUnparseableEntries) { - temp = new FTPFile(entry); - } - if (filter.accept(temp)) { - tmpResults.add(temp); - } - } - return tmpResults.toArray(new FTPFile[tmpResults.size()]); - } - - /** - * convenience method to allow clients to know whether this object's - * internal iterator's current position is at the end of the list. - * - * @return true if internal iterator is not at end of list, false - * otherwise. - */ - public boolean hasNext() { - return _internalIterator.hasNext(); - } - - /** - * convenience method to allow clients to know whether this object's - * internal iterator's current position is at the beginning of the list. - * - * @return true if internal iterator is not at beginning of list, false - * otherwise. - */ - public boolean hasPrevious() { - return _internalIterator.hasPrevious(); - } - - /** - * resets this object's internal iterator to the beginning of the list. - */ - public void resetIterator() { - this._internalIterator = this.entries.listIterator(); - } - - // DEPRECATED METHODS - for API compatibility only - DO NOT USE - - /** - * Do not use. - * - * @param stream the stream from which to read - * @throws IOException on error - * @deprecated use {@link #readServerList(InputStream, String)} instead - */ - @Deprecated public void readServerList(InputStream stream) throws IOException { - readServerList(stream, null); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPReply.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPReply.java deleted file mode 100644 index 991cb93d..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPReply.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -/*** - * FTPReply stores a set of constants for FTP reply codes. To interpret - * the meaning of the codes, familiarity with RFC 959 is assumed. - * The mnemonic constant names are transcriptions from the code descriptions - * of RFC 959. - *

- * TODO replace with an enum - ***/ - -public final class FTPReply { - - public static final int RESTART_MARKER = 110; - public static final int SERVICE_NOT_READY = 120; - public static final int DATA_CONNECTION_ALREADY_OPEN = 125; - public static final int FILE_STATUS_OK = 150; - public static final int COMMAND_OK = 200; - public static final int COMMAND_IS_SUPERFLUOUS = 202; - public static final int SYSTEM_STATUS = 211; - public static final int DIRECTORY_STATUS = 212; - public static final int FILE_STATUS = 213; - public static final int HELP_MESSAGE = 214; - public static final int NAME_SYSTEM_TYPE = 215; - public static final int SERVICE_READY = 220; - public static final int SERVICE_CLOSING_CONTROL_CONNECTION = 221; - public static final int DATA_CONNECTION_OPEN = 225; - public static final int CLOSING_DATA_CONNECTION = 226; - public static final int ENTERING_PASSIVE_MODE = 227; - /** @since 2.2 */ - public static final int ENTERING_EPSV_MODE = 229; - public static final int USER_LOGGED_IN = 230; - public static final int FILE_ACTION_OK = 250; - public static final int PATHNAME_CREATED = 257; - public static final int NEED_PASSWORD = 331; - public static final int NEED_ACCOUNT = 332; - public static final int FILE_ACTION_PENDING = 350; - public static final int SERVICE_NOT_AVAILABLE = 421; - public static final int CANNOT_OPEN_DATA_CONNECTION = 425; - public static final int TRANSFER_ABORTED = 426; - public static final int FILE_ACTION_NOT_TAKEN = 450; - public static final int ACTION_ABORTED = 451; - public static final int INSUFFICIENT_STORAGE = 452; - public static final int UNRECOGNIZED_COMMAND = 500; - public static final int SYNTAX_ERROR_IN_ARGUMENTS = 501; - public static final int COMMAND_NOT_IMPLEMENTED = 502; - public static final int BAD_COMMAND_SEQUENCE = 503; - public static final int COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504; - public static final int NOT_LOGGED_IN = 530; - public static final int NEED_ACCOUNT_FOR_STORING_FILES = 532; - public static final int FILE_UNAVAILABLE = 550; - public static final int PAGE_TYPE_UNKNOWN = 551; - public static final int STORAGE_ALLOCATION_EXCEEDED = 552; - public static final int FILE_NAME_NOT_ALLOWED = 553; - - // FTPS Reply Codes - - /** @since 2.0 */ - public static final int SECURITY_DATA_EXCHANGE_COMPLETE = 234; - /** @since 2.0 */ - public static final int SECURITY_DATA_EXCHANGE_SUCCESSFULLY = 235; - /** @since 2.0 */ - public static final int SECURITY_MECHANISM_IS_OK = 334; - /** @since 2.0 */ - public static final int SECURITY_DATA_IS_ACCEPTABLE = 335; - /** @since 2.0 */ - public static final int UNAVAILABLE_RESOURCE = 431; - /** @since 2.2 */ - public static final int BAD_TLS_NEGOTIATION_OR_DATA_ENCRYPTION_REQUIRED = 522; - /** @since 2.0 */ - public static final int DENIED_FOR_POLICY_REASONS = 533; - /** @since 2.0 */ - public static final int REQUEST_DENIED = 534; - /** @since 2.0 */ - public static final int FAILED_SECURITY_CHECK = 535; - /** @since 2.0 */ - public static final int REQUESTED_PROT_LEVEL_NOT_SUPPORTED = 536; - - // IPv6 error codes - // Note this is also used as an FTPS error code reply - /** @since 2.2 */ - public static final int EXTENDED_PORT_FAILURE = 522; - - // Cannot be instantiated - private FTPReply() { - } - - /*** - * Determine if a reply code is a positive preliminary response. All - * codes beginning with a 1 are positive preliminary responses. - * Postitive preliminary responses are used to indicate tentative success. - * No further commands can be issued to the FTP server after a positive - * preliminary response until a follow up response is received from the - * server. - * - * @param reply The reply code to test. - * @return True if a reply code is a postive preliminary response, false - * if not. - ***/ - public static boolean isPositivePreliminary(int reply) { - return (reply >= 100 && reply < 200); - } - - /*** - * Determine if a reply code is a positive completion response. All - * codes beginning with a 2 are positive completion responses. - * The FTP server will send a positive completion response on the final - * successful completion of a command. - * - * @param reply The reply code to test. - * @return True if a reply code is a postive completion response, false - * if not. - ***/ - public static boolean isPositiveCompletion(int reply) { - return (reply >= 200 && reply < 300); - } - - /*** - * Determine if a reply code is a positive intermediate response. All - * codes beginning with a 3 are positive intermediate responses. - * The FTP server will send a positive intermediate response on the - * successful completion of one part of a multi-part sequence of - * commands. For example, after a successful USER command, a positive - * intermediate response will be sent to indicate that the server is - * ready for the PASS command. - * - * @param reply The reply code to test. - * @return True if a reply code is a postive intermediate response, false - * if not. - ***/ - public static boolean isPositiveIntermediate(int reply) { - return (reply >= 300 && reply < 400); - } - - /*** - * Determine if a reply code is a negative transient response. All - * codes beginning with a 4 are negative transient responses. - * The FTP server will send a negative transient response on the - * failure of a command that can be reattempted with success. - * - * @param reply The reply code to test. - * @return True if a reply code is a negative transient response, false - * if not. - ***/ - public static boolean isNegativeTransient(int reply) { - return (reply >= 400 && reply < 500); - } - - /*** - * Determine if a reply code is a negative permanent response. All - * codes beginning with a 5 are negative permanent responses. - * The FTP server will send a negative permanent response on the - * failure of a command that cannot be reattempted with success. - * - * @param reply The reply code to test. - * @return True if a reply code is a negative permanent response, false - * if not. - ***/ - public static boolean isNegativePermanent(int reply) { - return (reply >= 500 && reply < 600); - } - - /** - * Determine if a reply code is a protected response. - * - * @param reply The reply code to test. - * @return True if a reply code is a protected response, false - * if not. - * @since 3.0 - */ - public static boolean isProtectedReplyCode(int reply) { - // actually, only 3 protected reply codes are - // defined in RFC 2228: 631, 632 and 633. - return (reply >= 600 && reply < 700); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java deleted file mode 100644 index 7f79e98b..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSClient.java +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import aria.apache.commons.net.SocketClient; -import aria.apache.commons.net.util.KeyManagerUtils; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; - -import aria.apache.commons.net.util.Base64; -import aria.apache.commons.net.util.SSLContextUtils; -import aria.apache.commons.net.util.SSLSocketUtils; -import aria.apache.commons.net.util.TrustManagerUtils; - -/** - * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to - * see wire-level SSL details. - * - * Warning: the hostname is not verified against the certificate by default, use - * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)} - * (on Java 1.7+) to enable verification. Verification is only performed on client mode connections. - * - * @version $Id: FTPSClient.java 1747829 2016-06-11 00:57:57Z sebb $ - * @since 2.0 - */ -public class FTPSClient extends FTPClient { - - // From http://www.iana.org/assignments/port-numbers - - // ftps-data 989/tcp ftp protocol, data, over TLS/SSL - // ftps-data 989/udp ftp protocol, data, over TLS/SSL - // ftps 990/tcp ftp protocol, control, over TLS/SSL - // ftps 990/udp ftp protocol, control, over TLS/SSL - - public static final int DEFAULT_FTPS_DATA_PORT = 989; - public static final int DEFAULT_FTPS_PORT = 990; - - /** The value that I can set in PROT command (C = Clear, P = Protected) */ - private static final String[] PROT_COMMAND_VALUE = { "C", "E", "S", "P" }; - /** Default PROT Command */ - private static final String DEFAULT_PROT = "C"; - /** Default secure socket protocol name, i.e. TLS */ - private static final String DEFAULT_PROTOCOL = "TLS"; - - /** The AUTH (Authentication/Security Mechanism) command. */ - private static final String CMD_AUTH = "AUTH"; - /** The ADAT (Authentication/Security Data) command. */ - private static final String CMD_ADAT = "ADAT"; - /** The PROT (Data Channel Protection Level) command. */ - private static final String CMD_PROT = "PROT"; - /** The PBSZ (Protection Buffer Size) command. */ - private static final String CMD_PBSZ = "PBSZ"; - /** The MIC (Integrity Protected Command) command. */ - private static final String CMD_MIC = "MIC"; - /** The CONF (Confidentiality Protected Command) command. */ - private static final String CMD_CONF = "CONF"; - /** The ENC (Privacy Protected Command) command. */ - private static final String CMD_ENC = "ENC"; - /** The CCC (Clear Command Channel) command. */ - private static final String CMD_CCC = "CCC"; - - /** The security mode. (True - Implicit Mode / False - Explicit Mode) */ - private final boolean isImplicit; - /** The secure socket protocol to be used, e.g. SSL/TLS. */ - private final String protocol; - /** The AUTH Command value */ - private String auth = DEFAULT_PROTOCOL; - /** The context object. */ - private SSLContext context; - /** The socket object. */ - private Socket plainSocket; - /** Controls whether a new SSL session may be established by this socket. Default true. */ - private boolean isCreation = true; - /** The use client mode flag. */ - private boolean isClientMode = true; - /** The need client auth flag. */ - private boolean isNeedClientAuth = false; - /** The want client auth flag. */ - private boolean isWantClientAuth = false; - /** The cipher suites */ - private String[] suites = null; - /** The protocol versions */ - private String[] protocols = null; - - /** - * The FTPS {@link TrustManager} implementation, default validate only - * {@link TrustManagerUtils#getValidateServerCertificateTrustManager()}. - */ - private TrustManager trustManager = TrustManagerUtils.getValidateServerCertificateTrustManager(); - - /** The {@link KeyManager}, default null (i.e. use system default). */ - private KeyManager keyManager = null; - - /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */ - private HostnameVerifier hostnameVerifier = null; - - /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */ - private boolean tlsEndpointChecking; - - /** - * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}. - * - * Sets protocol to {@link #DEFAULT_PROTOCOL} - i.e. TLS - and security mode to explicit - * (isImplicit = false) - */ - public FTPSClient() { - this(DEFAULT_PROTOCOL, false); - } - - /** - * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS - * Calls {@link #FTPSClient(String, boolean)} - * - * @param isImplicit The security mode (Implicit/Explicit). - */ - public FTPSClient(boolean isImplicit) { - this(DEFAULT_PROTOCOL, isImplicit); - } - - /** - * Constructor for FTPSClient, using explict mode, calls {@link #FTPSClient(String, boolean)}. - * - * @param protocol the protocol to use - */ - public FTPSClient(String protocol) { - this(protocol, false); - } - - /** - * Constructor for FTPSClient allowing specification of protocol - * and security mode. If isImplicit is true, the port is set to - * {@link #DEFAULT_FTPS_PORT} i.e. 990. - * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} - * - * @param protocol the protocol - * @param isImplicit The security mode(Implicit/Explicit). - */ - public FTPSClient(String protocol, boolean isImplicit) { - super(); - this.protocol = protocol; - this.isImplicit = isImplicit; - if (isImplicit) { - setDefaultPort(DEFAULT_FTPS_PORT); - } - } - - /** - * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS - * The default TrustManager is set from {@link TrustManagerUtils#getValidateServerCertificateTrustManager()} - * - * @param isImplicit The security mode(Implicit/Explicit). - * @param context A pre-configured SSL Context - */ - public FTPSClient(boolean isImplicit, SSLContext context) { - this(DEFAULT_PROTOCOL, isImplicit); - this.context = context; - } - - /** - * Constructor for FTPSClient, using {@link #DEFAULT_PROTOCOL} - i.e. TLS - * and isImplicit {@code false} - * Calls {@link #FTPSClient(boolean, SSLContext)} - * - * @param context A pre-configured SSL Context - */ - public FTPSClient(SSLContext context) { - this(false, context); - } - - /** - * Set AUTH command use value. - * This processing is done before connected processing. - * - * @param auth AUTH command use value. - */ - public void setAuthValue(String auth) { - this.auth = auth; - } - - /** - * Return AUTH command use value. - * - * @return AUTH command use value. - */ - public String getAuthValue() { - return this.auth; - } - - /** - * Because there are so many connect() methods, - * the _connectAction_() method is provided as a means of performing - * some action immediately after establishing a connection, - * rather than reimplementing all of the connect() methods. - * - * @throws IOException If it throw by _connectAction_. - * @see SocketClient#_connectAction_() - */ - @Override protected void _connectAction_() throws IOException { - // Implicit mode. - if (isImplicit) { - sslNegotiation(); - } - super._connectAction_(); - // Explicit mode. - if (!isImplicit) { - execAUTH(); - sslNegotiation(); - } - } - - /** - * AUTH command. - * - * @throws SSLException If it server reply code not equal "234" and "334". - * @throws IOException If an I/O error occurs while either sending - * the command. - */ - protected void execAUTH() throws SSLException, IOException { - int replyCode = sendCommand(CMD_AUTH, auth); - if (FTPReply.SECURITY_MECHANISM_IS_OK == replyCode) { - // replyCode = 334 - // I carry out an ADAT command. - } else if (FTPReply.SECURITY_DATA_EXCHANGE_COMPLETE != replyCode) { - throw new SSLException(getReplyString()); - } - } - - /** - * Performs a lazy init of the SSL context - * - * @throws IOException - */ - private void initSslContext() throws IOException { - if (context == null) { - context = SSLContextUtils.createSSLContext(protocol, getKeyManager(), getTrustManager()); - } - } - - /** - * SSL/TLS negotiation. Acquires an SSL socket of a control - * connection and carries out handshake processing. - * - * @throws IOException If server negotiation fails - */ - protected void sslNegotiation() throws IOException { - plainSocket = _socket_; - initSslContext(); - - SSLSocketFactory ssf = context.getSocketFactory(); - String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress(); - int port = _socket_.getPort(); - SSLSocket socket = (SSLSocket) ssf.createSocket(_socket_, host, port, false); - socket.setEnableSessionCreation(isCreation); - socket.setUseClientMode(isClientMode); - - // client mode - if (isClientMode) { - if (tlsEndpointChecking) { - SSLSocketUtils.enableEndpointNameVerification(socket); - } - } else { // server mode - socket.setNeedClientAuth(isNeedClientAuth); - socket.setWantClientAuth(isWantClientAuth); - } - - if (protocols != null) { - socket.setEnabledProtocols(protocols); - } - if (suites != null) { - socket.setEnabledCipherSuites(suites); - } - socket.startHandshake(); - - // TODO the following setup appears to duplicate that in the super class methods - _socket_ = socket; - _controlInput_ = - new BufferedReader(new InputStreamReader(socket.getInputStream(), getControlEncoding())); - _controlOutput_ = - new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), getControlEncoding())); - - if (isClientMode) { - if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) { - throw new SSLHandshakeException("Hostname doesn't match certificate"); - } - } - } - - /** - * Get the {@link KeyManager} instance. - * - * @return The {@link KeyManager} instance - */ - private KeyManager getKeyManager() { - return keyManager; - } - - /** - * Set a {@link KeyManager} to use - * - * @param keyManager The KeyManager implementation to set. - * @see KeyManagerUtils - */ - public void setKeyManager(KeyManager keyManager) { - this.keyManager = keyManager; - } - - /** - * Controls whether a new SSL session may be established by this socket. - * - * @param isCreation The established socket flag. - */ - public void setEnabledSessionCreation(boolean isCreation) { - this.isCreation = isCreation; - } - - /** - * Returns true if new SSL sessions may be established by this socket. - * When the underlying {@link Socket} instance is not SSL-enabled (i.e. an - * instance of {@link SSLSocket} with {@link SSLSocket}{@link #getEnableSessionCreation()}) - * enabled, - * this returns False. - * - * @return true - Indicates that sessions may be created; - * this is the default. - * false - indicates that an existing session must be resumed. - */ - public boolean getEnableSessionCreation() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getEnableSessionCreation(); - } - return false; - } - - /** - * Configures the socket to require client authentication. - * - * @param isNeedClientAuth The need client auth flag. - */ - public void setNeedClientAuth(boolean isNeedClientAuth) { - this.isNeedClientAuth = isNeedClientAuth; - } - - /** - * Returns true if the socket will require client authentication. - * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. - * - * @return true - If the server mode socket should request - * that the client authenticate itself. - */ - public boolean getNeedClientAuth() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getNeedClientAuth(); - } - return false; - } - - /** - * Configures the socket to request client authentication, - * but only if such a request is appropriate to the cipher - * suite negotiated. - * - * @param isWantClientAuth The want client auth flag. - */ - public void setWantClientAuth(boolean isWantClientAuth) { - this.isWantClientAuth = isWantClientAuth; - } - - /** - * Returns true if the socket will request client authentication. - * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. - * - * @return true - If the server mode socket should request - * that the client authenticate itself. - */ - public boolean getWantClientAuth() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getWantClientAuth(); - } - return false; - } - - /** - * Configures the socket to use client (or server) mode in its first - * handshake. - * - * @param isClientMode The use client mode flag. - */ - public void setUseClientMode(boolean isClientMode) { - this.isClientMode = isClientMode; - } - - /** - * Returns true if the socket is set to use client mode - * in its first handshake. - * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns false. - * - * @return true - If the socket should start its first handshake - * in "client" mode. - */ - public boolean getUseClientMode() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getUseClientMode(); - } - return false; - } - - /** - * Controls which particular cipher suites are enabled for use on this - * connection. Called before server negotiation. - * - * @param cipherSuites The cipher suites. - */ - public void setEnabledCipherSuites(String[] cipherSuites) { - suites = new String[cipherSuites.length]; - System.arraycopy(cipherSuites, 0, suites, 0, cipherSuites.length); - } - - /** - * Returns the names of the cipher suites which could be enabled - * for use on this connection. - * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. - * - * @return An array of cipher suite names, or null - */ - public String[] getEnabledCipherSuites() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getEnabledCipherSuites(); - } - return null; - } - - /** - * Controls which particular protocol versions are enabled for use on this - * connection. I perform setting before a server negotiation. - * - * @param protocolVersions The protocol versions. - */ - public void setEnabledProtocols(String[] protocolVersions) { - protocols = new String[protocolVersions.length]; - System.arraycopy(protocolVersions, 0, protocols, 0, protocolVersions.length); - } - - /** - * Returns the names of the protocol versions which are currently - * enabled for use on this connection. - * When the underlying {@link Socket} is not an {@link SSLSocket} instance, returns null. - * - * @return An array of protocols, or null - */ - public String[] getEnabledProtocols() { - if (_socket_ instanceof SSLSocket) { - return ((SSLSocket) _socket_).getEnabledProtocols(); - } - return null; - } - - /** - * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. - * - * @param pbsz Protection Buffer Size. - * @throws SSLException If the server reply code does not equal "200". - * @throws IOException If an I/O error occurs while sending - * the command. - * @see #parsePBSZ(long) - */ - public void execPBSZ(long pbsz) throws SSLException, IOException { - if (pbsz < 0 || 4294967295L < pbsz) { // 32-bit unsigned number - throw new IllegalArgumentException(); - } - int status = sendCommand(CMD_PBSZ, String.valueOf(pbsz)); - if (FTPReply.COMMAND_OK != status) { - throw new SSLException(getReplyString()); - } - } - - /** - * PBSZ command. pbsz value: 0 to (2^32)-1 decimal integer. - * Issues the command and parses the response to return the negotiated value. - * - * @param pbsz Protection Buffer Size. - * @return the negotiated value. - * @throws SSLException If the server reply code does not equal "200". - * @throws IOException If an I/O error occurs while sending - * the command. - * @see #execPBSZ(long) - * @since 3.0 - */ - public long parsePBSZ(long pbsz) throws SSLException, IOException { - execPBSZ(pbsz); - long minvalue = pbsz; - String remainder = extractPrefixedData("PBSZ=", getReplyString()); - if (remainder != null) { - long replysz = Long.parseLong(remainder); - if (replysz < minvalue) { - minvalue = replysz; - } - } - return minvalue; - } - - /** - * PROT command. - *

    - *
  • C - Clear
  • - *
  • S - Safe(SSL protocol only)
  • - *
  • E - Confidential(SSL protocol only)
  • - *
  • P - Private
  • - *
- * N.B. the method calls - * {@link #setSocketFactory(javax.net.SocketFactory)} and - * {@link #setServerSocketFactory(javax.net.ServerSocketFactory)} - * - * @param prot Data Channel Protection Level, if {@code null}, use {@link #DEFAULT_PROT}. - * @throws SSLException If the server reply code does not equal {@code 200}. - * @throws IOException If an I/O error occurs while sending - * the command. - */ - public void execPROT(String prot) throws SSLException, IOException { - if (prot == null) { - prot = DEFAULT_PROT; - } - if (!checkPROTValue(prot)) { - throw new IllegalArgumentException(); - } - if (FTPReply.COMMAND_OK != sendCommand(CMD_PROT, prot)) { - throw new SSLException(getReplyString()); - } - if (DEFAULT_PROT.equals(prot)) { - setSocketFactory(null); - setServerSocketFactory(null); - } else { - setSocketFactory(new FTPSSocketFactory(context)); - setServerSocketFactory(new FTPSServerSocketFactory(context)); - initSslContext(); - } - } - - /** - * Check the value that can be set in PROT Command value. - * - * @param prot Data Channel Protection Level. - * @return True - A set point is right / False - A set point is not right - */ - private boolean checkPROTValue(String prot) { - for (String element : PROT_COMMAND_VALUE) { - if (element.equals(prot)) { - return true; - } - } - return false; - } - - /** - * Send an FTP command. - * A successful CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} - * instance to be assigned to a plain {@link Socket} - * - * @param command The FTP command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending the command. - * @throws SSLException if a CCC command fails - * @see FTP#sendCommand(String) - */ - // Would like to remove this method, but that will break any existing clients that are using CCC - @Override public int sendCommand(String command, String args) throws IOException { - int repCode = super.sendCommand(command, args); - /* If CCC is issued, restore socket i/o streams to unsecured versions */ - if (CMD_CCC.equals(command)) { - if (FTPReply.COMMAND_OK == repCode) { - _socket_.close(); - _socket_ = plainSocket; - _controlInput_ = new BufferedReader( - new InputStreamReader(_socket_.getInputStream(), getControlEncoding())); - _controlOutput_ = new BufferedWriter( - new OutputStreamWriter(_socket_.getOutputStream(), getControlEncoding())); - } else { - throw new SSLException(getReplyString()); - } - } - return repCode; - } - - /** - * Returns a socket of the data connection. - * Wrapped as an {@link SSLSocket}, which carries out handshake processing. - * - * @param command The int representation of the FTP command to send. - * @param arg The arguments to the FTP command. - * If this parameter is set to null, then the command is sent with - * no arguments. - * @return corresponding to the established data connection. - * Null is returned if an FTP protocol error is reported at any point - * during the establishment and initialization of the connection. - * @throws IOException If there is any problem with the connection. - * @see FTPClient#_openDataConnection_(int, String) - * @deprecated (3.3) Use {@link FTPClient#_openDataConnection_(FTPCmd, String)} instead - */ - @Override - // Strictly speaking this is not needed, but it works round a Clirr bug - // So rather than invoke the parent code, we do it here - @Deprecated protected Socket _openDataConnection_(int command, String arg) throws IOException { - return _openDataConnection_(FTPCommand.getCommand(command), arg); - } - - /** - * Returns a socket of the data connection. - * Wrapped as an {@link SSLSocket}, which carries out handshake processing. - * - * @param command The textual representation of the FTP command to send. - * @param arg The arguments to the FTP command. - * If this parameter is set to null, then the command is sent with - * no arguments. - * @return corresponding to the established data connection. - * Null is returned if an FTP protocol error is reported at any point - * during the establishment and initialization of the connection. - * @throws IOException If there is any problem with the connection. - * @see FTPClient#_openDataConnection_(int, String) - * @since 3.2 - */ - @Override protected Socket _openDataConnection_(String command, String arg) throws IOException { - Socket socket = super._openDataConnection_(command, arg); - _prepareDataSocket_(socket); - if (socket instanceof SSLSocket) { - SSLSocket sslSocket = (SSLSocket) socket; - - sslSocket.setUseClientMode(isClientMode); - sslSocket.setEnableSessionCreation(isCreation); - - // server mode - if (!isClientMode) { - sslSocket.setNeedClientAuth(isNeedClientAuth); - sslSocket.setWantClientAuth(isWantClientAuth); - } - if (suites != null) { - sslSocket.setEnabledCipherSuites(suites); - } - if (protocols != null) { - sslSocket.setEnabledProtocols(protocols); - } - sslSocket.startHandshake(); - } - - return socket; - } - - /** - * Performs any custom initialization for a newly created SSLSocket (before - * the SSL handshake happens). - * Called by {@link #_openDataConnection_(int, String)} immediately - * after creating the socket. - * The default implementation is a no-op - * - * @param socket the socket to set up - * @throws IOException on error - * @since 3.1 - */ - protected void _prepareDataSocket_(Socket socket) throws IOException { - } - - /** - * Get the currently configured {@link TrustManager}. - * - * @return A TrustManager instance. - */ - public TrustManager getTrustManager() { - return trustManager; - } - - /** - * Override the default {@link TrustManager} to use; if set to {@code null}, - * the default TrustManager from the JVM will be used. - * - * @param trustManager The TrustManager implementation to set, may be {@code null} - * @see TrustManagerUtils - */ - public void setTrustManager(TrustManager trustManager) { - this.trustManager = trustManager; - } - - /** - * Get the currently configured {@link HostnameVerifier}. - * The verifier is only used on client mode connections. - * - * @return A HostnameVerifier instance. - * @since 3.4 - */ - public HostnameVerifier getHostnameVerifier() { - return hostnameVerifier; - } - - /** - * Override the default {@link HostnameVerifier} to use. - * The verifier is only used on client mode connections. - * - * @param newHostnameVerifier The HostnameVerifier implementation to set or null to - * disable. - * @since 3.4 - */ - public void setHostnameVerifier(HostnameVerifier newHostnameVerifier) { - hostnameVerifier = newHostnameVerifier; - } - - /** - * Return whether or not endpoint identification using the HTTPS algorithm - * on Java 1.7+ is enabled. The default behaviour is for this to be disabled. - * - * This check is only performed on client mode connections. - * - * @return True if enabled, false if not. - * @since 3.4 - */ - public boolean isEndpointCheckingEnabled() { - return tlsEndpointChecking; - } - - /** - * Automatic endpoint identification checking using the HTTPS algorithm - * is supported on Java 1.7+. The default behaviour is for this to be disabled. - * - * This check is only performed on client mode connections. - * - * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on - * Java 1.7+. - * @since 3.4 - */ - public void setEndpointCheckingEnabled(boolean enable) { - tlsEndpointChecking = enable; - } - - /** - * Closes the connection to the FTP server and restores - * connection parameters to the default values. - *

- * Calls {@code setSocketFactory(null)} and {@code setServerSocketFactory(null)} - * to reset the factories that may have been changed during the session, - * e.g. by {@link #execPROT(String)} - * - * @throws IOException If an error occurs while disconnecting. - * @since 3.0 - */ - @Override public void disconnect() throws IOException { - super.disconnect(); - if (plainSocket != null) { - plainSocket.close(); - } - setSocketFactory(null); - setServerSocketFactory(null); - } - - /** - * Send the AUTH command with the specified mechanism. - * - * @param mechanism The mechanism name to send with the command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execAUTH(String mechanism) throws IOException { - return sendCommand(CMD_AUTH, mechanism); - } - - /** - * Send the ADAT command with the specified authentication data. - * - * @param data The data to send with the command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execADAT(byte[] data) throws IOException { - if (data != null) { - return sendCommand(CMD_ADAT, Base64.encodeBase64StringUnChunked(data)); - } else { - return sendCommand(CMD_ADAT); - } - } - - /** - * Send the CCC command to the server. - * The CCC (Clear Command Channel) command causes the underlying {@link SSLSocket} instance to be - * assigned - * to a plain {@link Socket} instances - * - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execCCC() throws IOException { - int repCode = sendCommand(CMD_CCC); - // This will be performed by sendCommand(String, String) - // if (FTPReply.isPositiveCompletion(repCode)) { - // _socket_.close(); - // _socket_ = plainSocket; - // _controlInput_ = new BufferedReader( - // new InputStreamReader( - // _socket_.getInputStream(), getControlEncoding())); - // _controlOutput_ = new BufferedWriter( - // new OutputStreamWriter( - // _socket_.getOutputStream(), getControlEncoding())); - // } - return repCode; - } - - /** - * Send the MIC command with the specified data. - * - * @param data The data to send with the command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execMIC(byte[] data) throws IOException { - if (data != null) { - return sendCommand(CMD_MIC, Base64.encodeBase64StringUnChunked(data)); - } else { - return sendCommand(CMD_MIC, ""); // perhaps "=" or just sendCommand(String)? - } - } - - /** - * Send the CONF command with the specified data. - * - * @param data The data to send with the command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execCONF(byte[] data) throws IOException { - if (data != null) { - return sendCommand(CMD_CONF, Base64.encodeBase64StringUnChunked(data)); - } else { - return sendCommand(CMD_CONF, ""); // perhaps "=" or just sendCommand(String)? - } - } - - /** - * Send the ENC command with the specified data. - * - * @param data The data to send with the command. - * @return server reply. - * @throws IOException If an I/O error occurs while sending - * the command. - * @since 3.0 - */ - public int execENC(byte[] data) throws IOException { - if (data != null) { - return sendCommand(CMD_ENC, Base64.encodeBase64StringUnChunked(data)); - } else { - return sendCommand(CMD_ENC, ""); // perhaps "=" or just sendCommand(String)? - } - } - - /** - * Parses the given ADAT response line and base64-decodes the data. - * - * @param reply The ADAT reply to parse. - * @return the data in the reply, base64-decoded. - * @since 3.0 - */ - public byte[] parseADATReply(String reply) { - if (reply == null) { - return null; - } else { - return Base64.decodeBase64(extractPrefixedData("ADAT=", reply)); - } - } - - /** - * Extract the data from a reply with a prefix, e.g. PBSZ=1234 => 1234 - * - * @param prefix the prefix to find - * @param reply where to find the prefix - * @return the remainder of the string after the prefix, or null if the prefix was not present. - */ - private String extractPrefixedData(String prefix, String reply) { - int idx = reply.indexOf(prefix); - if (idx == -1) { - return null; - } - // N.B. Cannot use trim before substring as leading space would affect the offset. - return reply.substring(idx + prefix.length()).trim(); - } - - // DEPRECATED - for API compatibility only - DO NOT USE - - /** @deprecated - not used - may be removed in a future release */ - @Deprecated public static String KEYSTORE_ALGORITHM; - - /** @deprecated - not used - may be removed in a future release */ - @Deprecated public static String TRUSTSTORE_ALGORITHM; - - /** @deprecated - not used - may be removed in a future release */ - @Deprecated public static String PROVIDER; - - /** @deprecated - not used - may be removed in a future release */ - @Deprecated public static String STORE_TYPE; -} -/* kate: indent-width 4; replace-tabs on; */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java deleted file mode 100644 index 5937638b..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -/** - * FTPS-specific commands. - * - * @since 2.0 - * @deprecated 3.0 DO NOT USE - */ -@Deprecated public final class FTPSCommand { - public static final int AUTH = 0; - public static final int ADAT = 1; - public static final int PBSZ = 2; - public static final int PROT = 3; - public static final int CCC = 4; - - public static final int AUTHENTICATION_SECURITY_MECHANISM = AUTH; - public static final int AUTHENTICATION_SECURITY_DATA = ADAT; - public static final int PROTECTION_BUFFER_SIZE = PBSZ; - public static final int DATA_CHANNEL_PROTECTION_LEVEL = PROT; - public static final int CLEAR_COMMAND_CHANNEL = CCC; - - private static final String[] _commands = { "AUTH", "ADAT", "PBSZ", "PROT", "CCC" }; - - /** - * Retrieve the FTPS command string corresponding to a specified - * command code. - * - * @param command The command code. - * @return The FTPS command string corresponding to a specified - * command code. - */ - public static final String getCommand(int command) { - return _commands[command]; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java deleted file mode 100644 index 168067bc..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSServerSocketFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; - -import javax.net.ServerSocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLServerSocket; - -/** - * Server socket factory for FTPS connections. - * - * @since 2.2 - */ -public class FTPSServerSocketFactory extends ServerSocketFactory { - - /** Factory for secure socket factories */ - private final SSLContext context; - - public FTPSServerSocketFactory(SSLContext context) { - this.context = context; - } - - // Override the default superclass method - @Override public ServerSocket createServerSocket() throws IOException { - return init(this.context.getServerSocketFactory().createServerSocket()); - } - - @Override public ServerSocket createServerSocket(int port) throws IOException { - return init(this.context.getServerSocketFactory().createServerSocket(port)); - } - - @Override public ServerSocket createServerSocket(int port, int backlog) throws IOException { - return init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); - } - - @Override public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) - throws IOException { - return init(this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); - } - - /** - * Sets the socket so newly accepted connections will use SSL client mode. - * - * @param socket the SSLServerSocket to initialise - * @return the socket - * @throws ClassCastException if socket is not an instance of SSLServerSocket - */ - public ServerSocket init(ServerSocket socket) { - ((SSLServerSocket) socket).setUseClientMode(true); - return socket; - } -} - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java deleted file mode 100644 index 7bbf818c..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSSocketFactory.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; - -/** - * Implementation of org.apache.commons.net.SocketFactory - * - * @since 2.0 - */ -public class FTPSSocketFactory extends SocketFactory { - - private final SSLContext context; - - public FTPSSocketFactory(SSLContext context) { - this.context = context; - } - - // Override the default implementation - @Override public Socket createSocket() throws IOException { - return this.context.getSocketFactory().createSocket(); - } - - @Override public Socket createSocket(String address, int port) - throws UnknownHostException, IOException { - return this.context.getSocketFactory().createSocket(address, port); - } - - @Override public Socket createSocket(InetAddress address, int port) throws IOException { - return this.context.getSocketFactory().createSocket(address, port); - } - - @Override - public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) - throws UnknownHostException, IOException { - return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) - throws IOException { - return this.context.getSocketFactory().createSocket(address, port, localAddress, localPort); - } - - // DEPRECATED METHODS - for API compatibility only - DO NOT USE - - /** - * @param port the port - * @return the socket - * @throws IOException on error - * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int) instead} - */ - @Deprecated public java.net.ServerSocket createServerSocket(int port) throws IOException { - return this.init(this.context.getServerSocketFactory().createServerSocket(port)); - } - - /** - * @param port the port - * @param backlog the backlog - * @return the socket - * @throws IOException on error - * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int) instead} - */ - @Deprecated public java.net.ServerSocket createServerSocket(int port, int backlog) - throws IOException { - return this.init(this.context.getServerSocketFactory().createServerSocket(port, backlog)); - } - - /** - * @param port the port - * @param backlog the backlog - * @param ifAddress the interface - * @return the socket - * @throws IOException on error - * @deprecated (2.2) use {@link FTPSServerSocketFactory#createServerSocket(int, int, InetAddress) - * instead} - */ - @Deprecated public java.net.ServerSocket createServerSocket(int port, int backlog, - InetAddress ifAddress) throws IOException { - return this.init( - this.context.getServerSocketFactory().createServerSocket(port, backlog, ifAddress)); - } - - /** - * @param socket the socket - * @return the socket - * @throws IOException on error - * @deprecated (2.2) use {@link FTPSServerSocketFactory#init(java.net.ServerSocket)} - */ - @Deprecated public java.net.ServerSocket init(java.net.ServerSocket socket) throws IOException { - ((javax.net.ssl.SSLServerSocket) socket).setUseClientMode(true); - return socket; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java deleted file mode 100644 index 0923ac02..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/FTPSTrustManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp; - -import aria.apache.commons.net.util.TrustManagerUtils; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.X509TrustManager; - -/** - * Do not use. - * - * @since 2.0 - * @deprecated 3.0 use - * {@link TrustManagerUtils#getValidateServerCertificateTrustManager() - * TrustManagerUtils#getValidateServerCertificateTrustManager()} instead - */ -@Deprecated public class FTPSTrustManager implements X509TrustManager { - private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[] {}; - - /** - * No-op - */ - @Override public void checkClientTrusted(X509Certificate[] certificates, String authType) { - return; - } - - @Override public void checkServerTrusted(X509Certificate[] certificates, String authType) - throws CertificateException { - for (X509Certificate certificate : certificates) { - certificate.checkValidity(); - } - } - - @Override public X509Certificate[] getAcceptedIssuers() { - return EMPTY_X509CERTIFICATE_ARRAY; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java deleted file mode 100644 index f7adaf6d..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/OnFtpInputStreamListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package aria.apache.commons.net.ftp; - -import aria.apache.commons.net.io.CopyStreamListener; - -/** - * Created by AriaL on 2017/9/26. - * ftp 上传文件流事件监听 - */ -public interface OnFtpInputStreamListener { - /** - * {@link CopyStreamListener#bytesTransferred(long, int, long)} - * - * @param totalBytesTransferred 已经上传的文件长度 - * @param bytesTransferred 上传byte长度 - */ - void onFtpInputStream(FTPClient client, long totalBytesTransferred, int bytesTransferred, - long streamSize); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/package-info.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/package-info.java deleted file mode 100644 index f7df7cf0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * FTP and FTPS support classes - */ -package aria.apache.commons.net.ftp; \ No newline at end of file diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java deleted file mode 100644 index 638f67fb..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/CompositeFileEntryParser.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; - -/** - * This implementation allows to pack some FileEntryParsers together - * and handle the case where to returned dirstyle isnt clearly defined. - * The matching parser will be cached. - * If the cached parser wont match due to the server changed the dirstyle, - * a new matching parser will be searched. - */ -public class CompositeFileEntryParser extends FTPFileEntryParserImpl { - private final FTPFileEntryParser[] ftpFileEntryParsers; - private FTPFileEntryParser cachedFtpFileEntryParser; - - public CompositeFileEntryParser(FTPFileEntryParser[] ftpFileEntryParsers) { - super(); - - this.cachedFtpFileEntryParser = null; - this.ftpFileEntryParsers = ftpFileEntryParsers; - } - - @Override public FTPFile parseFTPEntry(String listEntry) { - if (cachedFtpFileEntryParser != null) { - FTPFile matched = cachedFtpFileEntryParser.parseFTPEntry(listEntry); - if (matched != null) { - return matched; - } - } else { - for (FTPFileEntryParser ftpFileEntryParser : ftpFileEntryParsers) { - FTPFile matched = ftpFileEntryParser.parseFTPEntry(listEntry); - if (matched != null) { - cachedFtpFileEntryParser = ftpFileEntryParser; - return matched; - } - } - } - return null; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java deleted file mode 100644 index 1d8988b6..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ConfigurableFTPFileEntryParserImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.Configurable; -import aria.apache.commons.net.ftp.FTPClientConfig; -import java.text.ParseException; -import java.util.Calendar; - -/** - *

- * This abstract class implements the common timestamp parsing - * algorithm for all the concrete parsers. Classes derived from - * this one will parse file listings via a supplied regular expression - * that pulls out the date portion as a separate string which is - * passed to the underlying {@link FTPTimestampParser delegate} to - * handle parsing of the file timestamp. - *

- * This class also implements the {@link Configurable Configurable} - * interface to allow the parser to be configured from the outside. - * - * @since 1.4 - */ -public abstract class ConfigurableFTPFileEntryParserImpl extends RegexFTPFileEntryParserImpl - implements Configurable { - - private final FTPTimestampParser timestampParser; - - /** - * constructor for this abstract class. - * - * @param regex Regular expression used main parsing of the - * file listing. - */ - public ConfigurableFTPFileEntryParserImpl(String regex) { - super(regex); - this.timestampParser = new FTPTimestampParserImpl(); - } - - /** - * constructor for this abstract class. - * - * @param regex Regular expression used main parsing of the - * file listing. - * @param flags the flags to apply, see - * {@link java.util.regex.Pattern#compile(String, int) Pattern#compile(String, int)}. Use 0 for - * none. - * @since 3.4 - */ - public ConfigurableFTPFileEntryParserImpl(String regex, int flags) { - super(regex, flags); - this.timestampParser = new FTPTimestampParserImpl(); - } - - /** - * This method is called by the concrete parsers to delegate - * timestamp parsing to the timestamp parser. - * - * @param timestampStr the timestamp string pulled from the - * file listing by the regular expression parser, to be submitted - * to the timestampParser for extracting the timestamp. - * @return a java.util.Calendar containing results of the - * timestamp parse. - * @throws ParseException on parse error - */ - public Calendar parseTimestamp(String timestampStr) throws ParseException { - return this.timestampParser.parseTimestamp(timestampStr); - } - - /** - * Implementation of the {@link Configurable Configurable} - * interface. Configures this parser by delegating to the - * underlying Configurable FTPTimestampParser implementation, ' - * passing it the supplied {@link FTPClientConfig FTPClientConfig} - * if that is non-null or a default configuration defined by - * each concrete subclass. - * - * @param config the configuration to be used to configure this parser. - * If it is null, a default configuration defined by - * each concrete subclass is used instead. - */ - @Override public void configure(FTPClientConfig config) { - if (this.timestampParser instanceof Configurable) { - FTPClientConfig defaultCfg = getDefaultConfiguration(); - if (config != null) { - if (null == config.getDefaultDateFormatStr()) { - config.setDefaultDateFormatStr(defaultCfg.getDefaultDateFormatStr()); - } - if (null == config.getRecentDateFormatStr()) { - config.setRecentDateFormatStr(defaultCfg.getRecentDateFormatStr()); - } - ((Configurable) this.timestampParser).configure(config); - } else { - ((Configurable) this.timestampParser).configure(defaultCfg); - } - } - } - - /** - * Each concrete subclass must define this member to create - * a default configuration to be used when that subclass is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for the subclass. - */ - protected abstract FTPClientConfig getDefaultConfiguration(); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java deleted file mode 100644 index cc499c71..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/DefaultFTPFileEntryParserFactory.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.Configurable; -import aria.apache.commons.net.ftp.FTPClient; -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.util.regex.Pattern; - -/** - * This is the default implementation of the - * FTPFileEntryParserFactory interface. This is the - * implementation that will be used by - * FTPClient.listFiles() - * if no other implementation has been specified. - * - * @see FTPClient#listFiles - * @see FTPClient#setParserFactory - */ -public class DefaultFTPFileEntryParserFactory implements FTPFileEntryParserFactory { - - // Match a plain Java Identifier - private static final String JAVA_IDENTIFIER = - "\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart})*"; - // Match a qualified name, e.g. a.b.c.Name - but don't allow the default package as that would allow "VMS"/"UNIX" etc. - private static final String JAVA_QUALIFIED_NAME = - "(" + JAVA_IDENTIFIER + "\\.)+" + JAVA_IDENTIFIER; - // Create the pattern, as it will be reused many times - private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME); - - /** - * This default implementation of the FTPFileEntryParserFactory - * interface works according to the following logic: - * First it attempts to interpret the supplied key as a fully - * qualified classname (default package is not allowed) of a class implementing the - * FTPFileEntryParser interface. If that succeeds, a parser - * object of this class is instantiated and is returned; - * otherwise it attempts to interpret the key as an identirier - * commonly used by the FTP SYST command to identify systems. - *

- * If key is not recognized as a fully qualified - * classname known to the system, this method will then attempt - * to see whether it contains a string identifying one of - * the known parsers. This comparison is case-insensitive. - * The intent here is where possible, to select as keys strings - * which are returned by the SYST command on the systems which - * the corresponding parser successfully parses. This enables - * this factory to be used in the auto-detection system. - * - * @param key should be a fully qualified classname corresponding to - * a class implementing the FTPFileEntryParser interface
- * OR
- * a string containing (case-insensitively) one of the - * following keywords: - *

    - *
  • {@link FTPClientConfig#SYST_UNIX UNIX}
  • - *
  • {@link FTPClientConfig#SYST_NT WINDOWS}
  • - *
  • {@link FTPClientConfig#SYST_OS2 OS/2}
  • - *
  • {@link FTPClientConfig#SYST_OS400 OS/400}
  • - *
  • {@link FTPClientConfig#SYST_AS400 AS/400}
  • - *
  • {@link FTPClientConfig#SYST_VMS VMS}
  • - *
  • {@link FTPClientConfig#SYST_MVS MVS}
  • - *
  • {@link FTPClientConfig#SYST_NETWARE NETWARE}
  • - *
  • {@link FTPClientConfig#SYST_L8 TYPE:L8}
  • - *
- * @return the FTPFileEntryParser corresponding to the supplied key. - * @throws ParserInitializationException thrown if for any reason the factory cannot resolve - * the supplied key into an FTPFileEntryParser. - * @see FTPFileEntryParser - */ - @Override public FTPFileEntryParser createFileEntryParser(String key) { - if (key == null) { - throw new ParserInitializationException("Parser key cannot be null"); - } - return createFileEntryParser(key, null); - } - - // Common method to process both key and config parameters. - private FTPFileEntryParser createFileEntryParser(String key, FTPClientConfig config) { - FTPFileEntryParser parser = null; - - // Is the key a possible class name? - if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { - try { - Class parserClass = Class.forName(key); - try { - parser = (FTPFileEntryParser) parserClass.newInstance(); - } catch (ClassCastException e) { - throw new ParserInitializationException(parserClass.getName() - + " does not implement the interface " - + "FTPFileEntryParser.", e); - } catch (Exception e) { - throw new ParserInitializationException("Error initializing parser", e); - } catch (ExceptionInInitializerError e) { - throw new ParserInitializationException("Error initializing parser", e); - } - } catch (ClassNotFoundException e) { - // OK, assume it is an alias - } - } - - if (parser == null) { // Now try for aliases - String ukey = key.toUpperCase(java.util.Locale.ENGLISH); - if (ukey.indexOf(FTPClientConfig.SYST_UNIX_TRIM_LEADING) >= 0) { - parser = new UnixFTPEntryParser(config, true); - } - // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it - else if (ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0) { - parser = new UnixFTPEntryParser(config, false); - } else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0) { - parser = new VMSVersioningFTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0) { - parser = createNTFTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0) { - parser = new OS2FTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 - || ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0) { - parser = createOS400FTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0) { - parser = new MVSFTPEntryParser(); // Does not currently support config parameter - } else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0) { - parser = new NetwareFTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_MACOS_PETER) >= 0) { - parser = new MacOsPeterFTPEntryParser(config); - } else if (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0) { - // L8 normally means Unix, but move it to the end for some L8 systems that aren't. - // This check should be last! - parser = new UnixFTPEntryParser(config); - } else { - throw new ParserInitializationException("Unknown parser type: " + key); - } - } - - if (parser instanceof Configurable) { - ((Configurable) parser).configure(config); - } - return parser; - } - - /** - *

Implementation extracts a key from the supplied - * {@link FTPClientConfig FTPClientConfig} - * parameter and creates an object implementing the - * interface FTPFileEntryParser and uses the supplied configuration - * to configure it. - *

- * Note that this method will generally not be called in scenarios - * that call for autodetection of parser type but rather, for situations - * where the user knows that the server uses a non-default configuration - * and knows what that configuration is. - *

- * - * @param config A {@link FTPClientConfig FTPClientConfig} - * used to configure the parser created - * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. - * @throws ParserInitializationException Thrown on any exception in instantiation - * @throws NullPointerException if {@code config} is {@code null} - * @since 1.4 - */ - @Override public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) - throws ParserInitializationException { - String key = config.getServerSystemKey(); - return createFileEntryParser(key, config); - } - - public FTPFileEntryParser createUnixFTPEntryParser() { - return new UnixFTPEntryParser(); - } - - public FTPFileEntryParser createVMSVersioningFTPEntryParser() { - return new VMSVersioningFTPEntryParser(); - } - - public FTPFileEntryParser createNetwareFTPEntryParser() { - return new NetwareFTPEntryParser(); - } - - public FTPFileEntryParser createNTFTPEntryParser() { - return createNTFTPEntryParser(null); - } - - /** - * Creates an NT FTP parser: if the config exists, and the system key equals - * {@link FTPClientConfig.SYST_NT} then a plain {@link NTFTPEntryParser} is used, - * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. - * - * @param config the config to use, may be {@code null} - * @return the parser - */ - private FTPFileEntryParser createNTFTPEntryParser(FTPClientConfig config) { - if (config != null && FTPClientConfig.SYST_NT.equals(config.getServerSystemKey())) { - return new NTFTPEntryParser(config); - } else { - // clone the config as it may be changed by the parsers (NET-602) - final FTPClientConfig config2 = (config != null) ? new FTPClientConfig(config) : null; - return new CompositeFileEntryParser(new FTPFileEntryParser[] { - new NTFTPEntryParser(config), new UnixFTPEntryParser(config2, - config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals( - config2.getServerSystemKey())) - }); - } - } - - public FTPFileEntryParser createOS2FTPEntryParser() { - return new OS2FTPEntryParser(); - } - - public FTPFileEntryParser createOS400FTPEntryParser() { - return createOS400FTPEntryParser(null); - } - - /** - * Creates an OS400 FTP parser: if the config exists, and the system key equals - * {@link FTPClientConfig.SYST_OS400} then a plain {@link OS400FTPEntryParser} is used, - * otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. - * - * @param config the config to use, may be {@code null} - * @return the parser - */ - private FTPFileEntryParser createOS400FTPEntryParser(FTPClientConfig config) { - if (config != null && FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) { - return new OS400FTPEntryParser(config); - } else { - // clone the config as it may be changed by the parsers (NET-602) - final FTPClientConfig config2 = (config != null) ? new FTPClientConfig(config) : null; - return new CompositeFileEntryParser(new FTPFileEntryParser[] { - new OS400FTPEntryParser(config), new UnixFTPEntryParser(config2, - config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals( - config2.getServerSystemKey())) - }); - } - } - - public FTPFileEntryParser createMVSEntryParser() { - return new MVSFTPEntryParser(); - } -} - diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java deleted file mode 100644 index ca31583a..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/EnterpriseUnixFTPEntryParser.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.util.Calendar; - -/** - * Parser for the Connect Enterprise Unix FTP Server From Sterling Commerce. - * Here is a sample of the sort of output line this parser processes: - * "-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST" - *

- * Note: EnterpriseUnixFTPEntryParser can only be instantiated through the - * DefaultFTPParserFactory by classname. It will not be chosen - * by the autodetection scheme. - * - * - * @version $Id: EnterpriseUnixFTPEntryParser.java 1741829 2016-05-01 00:24:44Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - * @see DefaultFTPFileEntryParserFactory - */ -public class EnterpriseUnixFTPEntryParser extends RegexFTPFileEntryParserImpl { - - /** - * months abbreviations looked for by this parser. Also used - * to determine which month has been matched by the parser. - */ - private static final String MONTHS = "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"; - - /** - * this is the regular expression used by this parser. - */ - private static final String REGEX = - "(([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])" - + "([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z])([\\-]|[A-Z]))" - + "(\\S*)\\s*" - // 12 - + "(\\S+)\\s*" - // 13 - + "(\\S*)\\s*" - // 14 user - + "(\\d*)\\s*" - // 15 group - + "(\\d*)\\s*" - // 16 filesize - + MONTHS - // 17 month - + "\\s*" - // TODO should the space be optional? - // TODO \\d* should be \\d? surely ? Otherwise 01111 is allowed - + "((?:[012]\\d*)|(?:3[01]))\\s*" - // 18 date [012]\d* or 3[01] - + "((\\d\\d\\d\\d)|((?:[01]\\d)|(?:2[0123])):([012345]\\d))\\s" - // 20 \d\d\d\d = year OR - // 21 [01]\d or 2[0123] hour + ':' - // 22 [012345]\d = minute - + "(\\S*)(\\s*.*)"; // 23 name - - /** - * The sole constructor for a EnterpriseUnixFTPEntryParser object. - */ - public EnterpriseUnixFTPEntryParser() { - super(REGEX); - } - - /** - * Parses a line of a unix FTP server file listing and converts it into a - * usable format in the form of an FTPFile instance. If - * the file listing line doesn't describe a file, null is - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - - FTPFile file = new FTPFile(); - file.setRawListing(entry); - - if (matches(entry)) { - String usr = group(14); - String grp = group(15); - String filesize = group(16); - String mo = group(17); - String da = group(18); - String yr = group(20); - String hr = group(21); - String min = group(22); - String name = group(23); - - file.setType(FTPFile.FILE_TYPE); - file.setUser(usr); - file.setGroup(grp); - try { - file.setSize(Long.parseLong(filesize)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.MILLISECOND, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.HOUR_OF_DAY, 0); - - int pos = MONTHS.indexOf(mo); - int month = pos / 4; - final int missingUnit; // the first missing unit - try { - - if (yr != null) { - // it's a year; there are no hours and minutes - cal.set(Calendar.YEAR, Integer.parseInt(yr)); - missingUnit = Calendar.HOUR_OF_DAY; - } else { - // it must be hour/minute or we wouldn't have matched - missingUnit = Calendar.SECOND; - int year = cal.get(Calendar.YEAR); - - // if the month we're reading is greater than now, it must - // be last year - if (cal.get(Calendar.MONTH) < month) { - year--; - } - cal.set(Calendar.YEAR, year); - cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hr)); - cal.set(Calendar.MINUTE, Integer.parseInt(min)); - } - cal.set(Calendar.MONTH, month); - cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(da)); - cal.clear(missingUnit); - file.setTimestamp(cal); - } catch (NumberFormatException e) { - // do nothing, date will be uninitialized - } - file.setName(name); - - return file; - } - return null; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java deleted file mode 100644 index 2e3c84e0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPFileEntryParserFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFileEntryParser; - -/** - * The interface describes a factory for creating FTPFileEntryParsers. - * - * @since 1.2 - */ -public interface FTPFileEntryParserFactory { - /** - * Implementation should be a method that decodes the - * supplied key and creates an object implementing the - * interface FTPFileEntryParser. - * - * @param key A string that somehow identifies an - * FTPFileEntryParser to be created. - * @return the FTPFileEntryParser created. - * @throws ParserInitializationException Thrown on any exception in instantiation - */ - public FTPFileEntryParser createFileEntryParser(String key) throws ParserInitializationException; - - /** - *

- * Implementation should be a method that extracts - * a key from the supplied {@link FTPClientConfig FTPClientConfig} - * parameter and creates an object implementing the - * interface FTPFileEntryParser and uses the supplied configuration - * to configure it. - *

- * Note that this method will generally not be called in scenarios - * that call for autodetection of parser type but rather, for situations - * where the user knows that the server uses a non-default configuration - * and knows what that configuration is. - *

- * - * @param config A {@link FTPClientConfig FTPClientConfig} - * used to configure the parser created - * @return the @link FTPFileEntryParser FTPFileEntryParser} so created. - * @throws ParserInitializationException Thrown on any exception in instantiation - * @since 1.4 - */ - public FTPFileEntryParser createFileEntryParser(FTPClientConfig config) - throws ParserInitializationException; -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java deleted file mode 100644 index dab122a4..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParser.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import java.text.ParseException; -import java.util.Calendar; - -/** - * This interface specifies the concept of parsing an FTP server's - * timestamp. - * - * @since 1.4 - */ -public interface FTPTimestampParser { - - /** - * the default default date format. - */ - public static final String DEFAULT_SDF = UnixFTPEntryParser.DEFAULT_DATE_FORMAT; - /** - * the default recent date format. - */ - public static final String DEFAULT_RECENT_SDF = UnixFTPEntryParser.DEFAULT_RECENT_DATE_FORMAT; - - /** - * Parses the supplied datestamp parameter. This parameter typically would - * have been pulled from a longer FTP listing via the regular expression - * mechanism - * - * @param timestampStr - the timestamp portion of the FTP directory listing - * to be parsed - * @return a java.util.Calendar object initialized to the date - * parsed by the parser - * @throws ParseException if none of the parser mechanisms belonging to - * the implementor can parse the input. - */ - public Calendar parseTimestamp(String timestampStr) throws ParseException; -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java deleted file mode 100644 index e84209ff..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.Configurable; -import aria.apache.commons.net.ftp.FTPClientConfig; -import java.text.DateFormatSymbols; -import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - -/** - * Default implementation of the {@link FTPTimestampParser FTPTimestampParser} - * interface also implements the {@link Configurable Configurable} - * interface to allow the parsing to be configured from the outside. - * - * @see ConfigurableFTPFileEntryParserImpl - * @since 1.4 - */ -public class FTPTimestampParserImpl implements FTPTimestampParser, Configurable { - - /** The date format for all dates, except possibly recent dates. Assumed to include the year. */ - private SimpleDateFormat defaultDateFormat; - /* The index in CALENDAR_UNITS of the smallest time unit in defaultDateFormat */ - private int defaultDateSmallestUnitIndex; - - /** The format used for recent dates (which don't have the year). May be null. */ - private SimpleDateFormat recentDateFormat; - /* The index in CALENDAR_UNITS of the smallest time unit in recentDateFormat */ - private int recentDateSmallestUnitIndex; - - private boolean lenientFutureDates = false; - - /* - * List of units in order of increasing significance. - * This allows the code to clear all units in the Calendar until it - * reaches the least significant unit in the parse string. - * The date formats are analysed to find the least significant - * unit (e.g. Minutes or Milliseconds) and the appropriate index to - * the array is saved. - * This is done by searching the array for the unit specifier, - * and returning the index. When clearing the Calendar units, - * the code loops through the array until the previous entry. - * e.g. for MINUTE it would clear MILLISECOND and SECOND - */ - private static final int[] CALENDAR_UNITS = { - Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, - Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR - }; - - /* - * Return the index to the array representing the least significant - * unit found in the date format. - * Default is 0 (to avoid dropping precision) - */ - private static int getEntry(SimpleDateFormat dateFormat) { - if (dateFormat == null) { - return 0; - } - final String FORMAT_CHARS = "SsmHdM"; - final String pattern = dateFormat.toPattern(); - for (char ch : FORMAT_CHARS.toCharArray()) { - if (pattern.indexOf(ch) != -1) { // found the character - switch (ch) { - case 'S': - return indexOf(Calendar.MILLISECOND); - case 's': - return indexOf(Calendar.SECOND); - case 'm': - return indexOf(Calendar.MINUTE); - case 'H': - return indexOf(Calendar.HOUR_OF_DAY); - case 'd': - return indexOf(Calendar.DAY_OF_MONTH); - case 'M': - return indexOf(Calendar.MONTH); - } - } - } - return 0; - } - - /* - * Find the entry in the CALENDAR_UNITS array. - */ - private static int indexOf(int calendarUnit) { - int i; - for (i = 0; i < CALENDAR_UNITS.length; i++) { - if (calendarUnit == CALENDAR_UNITS[i]) { - return i; - } - } - return 0; - } - - /* - * Sets the Calendar precision (used by FTPFile#toFormattedDate) by clearing - * the immediately preceeding unit (if any). - * Unfortunately the clear(int) method results in setting all other units. - */ - private static void setPrecision(int index, Calendar working) { - if (index <= 0) { // e.g. MILLISECONDS - return; - } - final int field = CALENDAR_UNITS[index - 1]; - // Just in case the analysis is wrong, stop clearing if - // field value is not the default. - final int value = working.get(field); - if (value != 0) { // don't reset if it has a value - // new Throwable("Unexpected value "+value).printStackTrace(); // DEBUG - } else { - working.clear(field); // reset just the required field - } - } - - /** - * The only constructor for this class. - */ - public FTPTimestampParserImpl() { - setDefaultDateFormat(DEFAULT_SDF, null); - setRecentDateFormat(DEFAULT_RECENT_SDF, null); - } - - /** - * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} - * in the {@link FTPTimestampParser FTPTimestampParser} interface - * according to this algorithm: - * - * If the recentDateFormat member has been defined, try to parse the - * supplied string with that. If that parse fails, or if the recentDateFormat - * member has not been defined, attempt to parse with the defaultDateFormat - * member. If that fails, throw a ParseException. - * - * This method assumes that the server time is the same as the local time. - * - * @param timestampStr The timestamp to be parsed - * @return a Calendar with the parsed timestamp - * @see FTPTimestampParserImpl#parseTimestamp(String, Calendar) - */ - @Override public Calendar parseTimestamp(String timestampStr) throws ParseException { - Calendar now = Calendar.getInstance(); - return parseTimestamp(timestampStr, now); - } - - /** - * If the recentDateFormat member has been defined, try to parse the - * supplied string with that. If that parse fails, or if the recentDateFormat - * member has not been defined, attempt to parse with the defaultDateFormat - * member. If that fails, throw a ParseException. - * - * This method allows a {@link Calendar} instance to be passed in which represents the - * current (system) time. - * - * @param timestampStr The timestamp to be parsed - * @param serverTime The current time for the server - * @return the calendar - * @throws ParseException if timestamp cannot be parsed - * @see FTPTimestampParser#parseTimestamp(String) - * @since 1.5 - */ - public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException { - Calendar working = (Calendar) serverTime.clone(); - working.setTimeZone(getServerTimeZone()); // is this needed? - - Date parsed = null; - - if (recentDateFormat != null) { - Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it - now.setTimeZone(this.getServerTimeZone()); - if (lenientFutureDates) { - // add a day to "now" so that "slop" doesn't cause a date - // slightly in the future to roll back a full year. (Bug 35181 => NET-83) - now.add(Calendar.DAY_OF_MONTH, 1); - } - // The Java SimpleDateFormat class uses the epoch year 1970 if not present in the input - // As 1970 was not a leap year, it cannot parse "Feb 29" correctly. - // Java 1.5+ returns Mar 1 1970 - // Temporarily add the current year to the short date time - // to cope with short-date leap year strings. - // Since Feb 29 is more that 6 months from the end of the year, this should be OK for - // all instances of short dates which are +- 6 months from current date. - // TODO this won't always work for systems that use short dates +0/-12months - // e.g. if today is Jan 1 2001 and the short date is Feb 29 - String year = Integer.toString(now.get(Calendar.YEAR)); - String timeStampStrPlusYear = timestampStr + " " + year; - SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy", - recentDateFormat.getDateFormatSymbols()); - hackFormatter.setLenient(false); - hackFormatter.setTimeZone(recentDateFormat.getTimeZone()); - ParsePosition pp = new ParsePosition(0); - parsed = hackFormatter.parse(timeStampStrPlusYear, pp); - // Check if we parsed the full string, if so it must have been a short date originally - if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) { - working.setTime(parsed); - if (working.after(now)) { // must have been last year instead - working.add(Calendar.YEAR, -1); - } - setPrecision(recentDateSmallestUnitIndex, working); - return working; - } - } - - ParsePosition pp = new ParsePosition(0); - parsed = defaultDateFormat.parse(timestampStr, pp); - // note, length checks are mandatory for us since - // SimpleDateFormat methods will succeed if less than - // full string is matched. They will also accept, - // despite "leniency" setting, a two-digit number as - // a valid year (e.g. 22:04 will parse as 22 A.D.) - // so could mistakenly confuse an hour with a year, - // if we don't insist on full length parsing. - if (parsed != null && pp.getIndex() == timestampStr.length()) { - working.setTime(parsed); - } else { - throw new ParseException("Timestamp '" - + timestampStr - + "' could not be parsed using a server time of " - + serverTime.getTime().toString(), pp.getErrorIndex()); - } - setPrecision(defaultDateSmallestUnitIndex, working); - return working; - } - - /** - * @return Returns the defaultDateFormat. - */ - public SimpleDateFormat getDefaultDateFormat() { - return defaultDateFormat; - } - - /** - * @return Returns the defaultDateFormat pattern string. - */ - public String getDefaultDateFormatString() { - return defaultDateFormat.toPattern(); - } - - /** - * @param format The defaultDateFormat to be set. - * @param dfs the symbols to use (may be null) - */ - private void setDefaultDateFormat(String format, DateFormatSymbols dfs) { - if (format != null) { - if (dfs != null) { - this.defaultDateFormat = new SimpleDateFormat(format, dfs); - } else { - this.defaultDateFormat = new SimpleDateFormat(format); - } - this.defaultDateFormat.setLenient(false); - } else { - this.defaultDateFormat = null; - } - this.defaultDateSmallestUnitIndex = getEntry(this.defaultDateFormat); - } - - /** - * @return Returns the recentDateFormat. - */ - public SimpleDateFormat getRecentDateFormat() { - return recentDateFormat; - } - - /** - * @return Returns the recentDateFormat. - */ - public String getRecentDateFormatString() { - return recentDateFormat.toPattern(); - } - - /** - * @param format The recentDateFormat to set. - * @param dfs the symbols to use (may be null) - */ - private void setRecentDateFormat(String format, DateFormatSymbols dfs) { - if (format != null) { - if (dfs != null) { - this.recentDateFormat = new SimpleDateFormat(format, dfs); - } else { - this.recentDateFormat = new SimpleDateFormat(format); - } - this.recentDateFormat.setLenient(false); - } else { - this.recentDateFormat = null; - } - this.recentDateSmallestUnitIndex = getEntry(this.recentDateFormat); - } - - /** - * @return returns an array of 12 strings representing the short - * month names used by this parse. - */ - public String[] getShortMonths() { - return defaultDateFormat.getDateFormatSymbols().getShortMonths(); - } - - /** - * @return Returns the serverTimeZone used by this parser. - */ - public TimeZone getServerTimeZone() { - return this.defaultDateFormat.getTimeZone(); - } - - /** - * sets a TimeZone represented by the supplied ID string into all - * of the parsers used by this server. - * - * @param serverTimeZone Time Id java.util.TimeZone id used by - * the ftp server. If null the client's local time zone is assumed. - */ - private void setServerTimeZone(String serverTimeZoneId) { - TimeZone serverTimeZone = TimeZone.getDefault(); - if (serverTimeZoneId != null) { - serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId); - } - this.defaultDateFormat.setTimeZone(serverTimeZone); - if (this.recentDateFormat != null) { - this.recentDateFormat.setTimeZone(serverTimeZone); - } - } - - /** - * Implementation of the {@link Configurable Configurable} - * interface. Configures this FTPTimestampParser according - * to the following logic: - *

- * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(String) defaultDateFormat} - * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} - * to values supplied in the config based on month names configured as follows: - *

- *
    - *
  • If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString} - * has been supplied in the config, use that to parse parse timestamps.
  • - *
  • Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode} - * has been supplied in the config, use the month names represented - * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language} - * to parse timestamps.
  • - *
  • otherwise use default English month names
  • - *

- * Finally if a {@link FTPClientConfig#setServerTimeZoneId(String) - * serverTimeZoneId} - * has been supplied via the config, set that into all date formats that have - * been configured. - *

- */ - @Override public void configure(FTPClientConfig config) { - DateFormatSymbols dfs = null; - - String languageCode = config.getServerLanguageCode(); - String shortmonths = config.getShortMonthNames(); - if (shortmonths != null) { - dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); - } else if (languageCode != null) { - dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); - } else { - dfs = FTPClientConfig.lookupDateFormatSymbols("en"); - } - - String recentFormatString = config.getRecentDateFormatStr(); - setRecentDateFormat(recentFormatString, dfs); - - String defaultFormatString = config.getDefaultDateFormatStr(); - if (defaultFormatString == null) { - throw new IllegalArgumentException("defaultFormatString cannot be null"); - } - setDefaultDateFormat(defaultFormatString, dfs); - - setServerTimeZone(config.getServerTimeZoneId()); - - this.lenientFutureDates = config.isLenientFutureDates(); - } - - /** - * @return Returns the lenientFutureDates. - */ - boolean isLenientFutureDates() { - return lenientFutureDates; - } - - /** - * @param lenientFutureDates The lenientFutureDates to set. - */ - void setLenientFutureDates(boolean lenientFutureDates) { - this.lenientFutureDates = lenientFutureDates; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java deleted file mode 100644 index b6d11bb8..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MLSxEntryParser.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Parser class for MSLT and MLSD replies. See RFC 3659. - *

- * Format is as follows: - *

- * entry            = [ facts ] SP pathname
- * facts            = 1*( fact ";" )
- * fact             = factname "=" value
- * factname         = "Size" / "Modify" / "Create" /
- *                    "Type" / "Unique" / "Perm" /
- *                    "Lang" / "Media-Type" / "CharSet" /
- * os-depend-fact / local-fact
- * os-depend-fact   = {IANA assigned OS name} "." token
- * local-fact       = "X." token
- * value            = *SCHAR
- *
- * Sample os-depend-fact:
- * UNIX.group=0;UNIX.mode=0755;UNIX.owner=0;
- * 
- * A single control response entry (MLST) is returned with a leading space; - * multiple (data) entries are returned without any leading spaces. - * The parser requires that the leading space from the MLST entry is removed. - * MLSD entries can begin with a single space if there are no facts. - * - * @since 3.0 - */ -public class MLSxEntryParser extends FTPFileEntryParserImpl { - // This class is immutable, so a single instance can be shared. - private static final MLSxEntryParser PARSER = new MLSxEntryParser(); - - private static final HashMap TYPE_TO_INT = new HashMap(); - - static { - TYPE_TO_INT.put("file", Integer.valueOf(FTPFile.FILE_TYPE)); - TYPE_TO_INT.put("cdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // listed directory - TYPE_TO_INT.put("pdir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // a parent dir - TYPE_TO_INT.put("dir", Integer.valueOf(FTPFile.DIRECTORY_TYPE)); // dir or sub-dir - } - - private static int UNIX_GROUPS[] = { // Groups in order of mode digits - FTPFile.USER_ACCESS, FTPFile.GROUP_ACCESS, FTPFile.WORLD_ACCESS, - }; - - private static int UNIX_PERMS[][] = { // perm bits, broken down by octal int value -/* 0 */ {}, -/* 1 */ { FTPFile.EXECUTE_PERMISSION }, -/* 2 */ { FTPFile.WRITE_PERMISSION }, -/* 3 */ { FTPFile.EXECUTE_PERMISSION, FTPFile.WRITE_PERMISSION }, -/* 4 */ { FTPFile.READ_PERMISSION }, -/* 5 */ { FTPFile.READ_PERMISSION, FTPFile.EXECUTE_PERMISSION }, -/* 6 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION }, -/* 7 */ { FTPFile.READ_PERMISSION, FTPFile.WRITE_PERMISSION, FTPFile.EXECUTE_PERMISSION }, - }; - - /** - * Create the parser for MSLT and MSLD listing entries - * This class is immutable, so one can use {@link #getInstance()} instead. - */ - public MLSxEntryParser() { - super(); - } - - @Override public FTPFile parseFTPEntry(String entry) { - if (entry.startsWith(" ")) {// leading space means no facts are present - if (entry.length() > 1) { // is there a path name? - FTPFile file = new FTPFile(); - file.setRawListing(entry); - file.setName(entry.substring(1)); - return file; - } else { - return null; // Invalid - no pathname - } - } - String parts[] = entry.split(" ", 2); // Path may contain space - if (parts.length != 2 || parts[1].length() == 0) { - return null; // no space found or no file name - } - final String factList = parts[0]; - if (!factList.endsWith(";")) { - return null; - } - FTPFile file = new FTPFile(); - file.setRawListing(entry); - file.setName(parts[1]); - String[] facts = factList.split(";"); - boolean hasUnixMode = parts[0].toLowerCase(Locale.ENGLISH).contains("unix.mode="); - for (String fact : facts) { - String[] factparts = fact.split("=", -1); // Don't drop empty values - // Sample missing permission - // drwx------ 2 mirror mirror 4096 Mar 13 2010 subversion - // modify=20100313224553;perm=;type=dir;unique=811U282598;UNIX.group=500;UNIX.mode=0700;UNIX.owner=500; subversion - if (factparts.length != 2) { - return null; // invalid - there was no "=" sign - } - String factname = factparts[0].toLowerCase(Locale.ENGLISH); - String factvalue = factparts[1]; - if (factvalue.length() == 0) { - continue; // nothing to see here - } - String valueLowerCase = factvalue.toLowerCase(Locale.ENGLISH); - if ("size".equals(factname)) { - file.setSize(Long.parseLong(factvalue)); - } else if ("sizd".equals(factname)) { // Directory size - file.setSize(Long.parseLong(factvalue)); - } else if ("modify".equals(factname)) { - final Calendar parsed = parseGMTdateTime(factvalue); - if (parsed == null) { - return null; - } - file.setTimestamp(parsed); - } else if ("type".equals(factname)) { - Integer intType = TYPE_TO_INT.get(valueLowerCase); - if (intType == null) { - file.setType(FTPFile.UNKNOWN_TYPE); - } else { - file.setType(intType.intValue()); - } - } else if (factname.startsWith("unix.")) { - String unixfact = factname.substring("unix.".length()).toLowerCase(Locale.ENGLISH); - if ("group".equals(unixfact)) { - file.setGroup(factvalue); - } else if ("owner".equals(unixfact)) { - file.setUser(factvalue); - } else if ("mode".equals(unixfact)) { // e.g. 0[1]755 - int off = factvalue.length() - 3; // only parse last 3 digits - for (int i = 0; i < 3; i++) { - int ch = factvalue.charAt(off + i) - '0'; - if (ch >= 0 && ch <= 7) { // Check it's valid octal - for (int p : UNIX_PERMS[ch]) { - file.setPermission(UNIX_GROUPS[i], p, true); - } - } else { - // TODO should this cause failure, or can it be reported somehow? - } - } // digits - } // mode - } // unix. - else if (!hasUnixMode && "perm".equals(factname)) { // skip if we have the UNIX.mode - doUnixPerms(file, valueLowerCase); - } // process "perm" - } // each fact - return file; - } - - /** - * Parse a GMT time stamp of the form YYYYMMDDHHMMSS[.sss] - * - * @param timestamp the date-time to parse - * @return a Calendar entry, may be {@code null} - * @since 3.4 - */ - public static Calendar parseGMTdateTime(String timestamp) { - final SimpleDateFormat sdf; - final boolean hasMillis; - if (timestamp.contains(".")) { - sdf = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); - hasMillis = true; - } else { - sdf = new SimpleDateFormat("yyyyMMddHHmmss"); - hasMillis = false; - } - TimeZone GMT = TimeZone.getTimeZone("GMT"); - // both timezones need to be set for the parse to work OK - sdf.setTimeZone(GMT); - GregorianCalendar gc = new GregorianCalendar(GMT); - ParsePosition pos = new ParsePosition(0); - sdf.setLenient(false); // We want to parse the whole string - final Date parsed = sdf.parse(timestamp, pos); - if (pos.getIndex() != timestamp.length()) { - return null; // did not fully parse the input - } - gc.setTime(parsed); - if (!hasMillis) { - gc.clear(Calendar.MILLISECOND); // flag up missing ms units - } - return gc; - } - - // perm-fact = "Perm" "=" *pvals - // pvals = "a" / "c" / "d" / "e" / "f" / - // "l" / "m" / "p" / "r" / "w" - private void doUnixPerms(FTPFile file, String valueLowerCase) { - for (char c : valueLowerCase.toCharArray()) { - // TODO these are mostly just guesses at present - switch (c) { - case 'a': // (file) may APPEnd - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'c': // (dir) files may be created in the dir - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'd': // deletable - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'e': // (dir) can change to this dir - file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); - break; - case 'f': // (file) renamable - // ?? file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'l': // (dir) can be listed - file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION, true); - break; - case 'm': // (dir) can create directory here - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'p': // (dir) entries may be deleted - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - case 'r': // (files) file may be RETRieved - file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); - break; - case 'w': // (files) file may be STORed - file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - break; - default: - break; - // ignore unexpected flag for now. - } // switch - } // each char - } - - public static FTPFile parseEntry(String entry) { - return PARSER.parseFTPEntry(entry); - } - - public static MLSxEntryParser getInstance() { - return PARSER; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java deleted file mode 100644 index 85a01925..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MVSFTPEntryParser.java +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; -import java.util.List; - -/** - * Implementation of FTPFileEntryParser and FTPFileListParser for IBM zOS/MVS - * Systems. - * - * @version $Id: MVSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for - * usage instructions) - */ -public class MVSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - static final int UNKNOWN_LIST_TYPE = -1; - static final int FILE_LIST_TYPE = 0; - static final int MEMBER_LIST_TYPE = 1; - static final int UNIX_LIST_TYPE = 2; - static final int JES_LEVEL_1_LIST_TYPE = 3; - static final int JES_LEVEL_2_LIST_TYPE = 4; - - private int isType = UNKNOWN_LIST_TYPE; - - /** - * Fallback parser for Unix-style listings - */ - private UnixFTPEntryParser unixFTPEntryParser; - - /** - * Dates are ignored for file lists, but are used for member lists where - * possible - */ - static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd HH:mm"; // 2001/09/18 - // 13:52 - - /** - * Matches these entries: - *
-   *  Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
-   *  B10142 3390   2006/03/20  2   31  F       80    80  PS   MDI.OKL.WORK
-   * 
- */ - static final String FILE_LIST_REGEX = "\\S+\\s+" + // volume - // ignored - "\\S+\\s+" + // unit - ignored - "\\S+\\s+" + // access date - ignored - "\\S+\\s+" + // extents -ignored - "\\S+\\s+" + // used - ignored - "[FV]\\S*\\s+" + // recfm - must start with F or V - "\\S+\\s+" + // logical record length -ignored - "\\S+\\s+" + // block size - ignored - "(PS|PO|PO-E)\\s+" + // Dataset organisation. Many exist - // but only support: PS, PO, PO-E - "(\\S+)\\s*"; // Dataset Name (file name) - - /** - * Matches these entries: - *
-   *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
-   *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
-   * 
- */ - static final String MEMBER_LIST_REGEX = "(\\S+)\\s+" + // name - "\\S+\\s+" + // version, modification (ignored) - "\\S+\\s+" + // create date (ignored) - "(\\S+)\\s+" + // modification date - "(\\S+)\\s+" + // modification time - "\\S+\\s+" + // size in lines (ignored) - "\\S+\\s+" + // size in lines at creation(ignored) - "\\S+\\s+" + // lines modified (ignored) - "\\S+\\s*"; // id of user who modified (ignored) - - /** - * Matches these entries, note: no header: - *
-   *   IBMUSER1  JOB01906  OUTPUT    3 Spool Files
-   *   012345678901234567890123456789012345678901234
-   *             1         2         3         4
-   * 
- */ - static final String JES_LEVEL_1_LIST_REGEX = "(\\S+)\\s+" + // job name ignored - "(\\S+)\\s+" + // job number - "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) - "(\\S+)\\s+" + // number of spool files - "(\\S+)\\s+" + // Text "Spool" ignored - "(\\S+)\\s*" // Text "Files" ignored - ; - - /** - * JES INTERFACE LEVEL 2 parser - * Matches these entries: - *
-   * JOBNAME  JOBID    OWNER    STATUS CLASS
-   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
-   * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
-   * 
- * Sample output from FTP session: - *
-   * ftp> quote site filetype=jes
-   * 200 SITE command was accepted
-   * ftp> ls
-   * 200 Port request OK.
-   * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
-   * JOBNAME  JOBID    OWNER    STATUS CLASS
-   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000 3 spool files
-   * IBMUSER  TSU01830 IBMUSER  OUTPUT TSU      ABEND=522 3 spool files
-   * 250 List completed successfully.
-   * ftp> ls job01906
-   * 200 Port request OK.
-   * 125 List started OK for JESJOBNAME=IBMUSER*, JESSTATUS=ALL and JESOWNER=IBMUSER
-   * JOBNAME  JOBID    OWNER    STATUS CLASS
-   * IBMUSER1 JOB01906 IBMUSER  OUTPUT A        RC=0000
-   * --------
-   * ID  STEPNAME PROCSTEP C DDNAME   BYTE-COUNT
-   * 001 JES2              A JESMSGLG       858
-   * 002 JES2              A JESJCL         128
-   * 003 JES2              A JESYSMSG       443
-   * 3 spool files
-   * 250 List completed successfully.
-   * 
- */ - - static final String JES_LEVEL_2_LIST_REGEX = "(\\S+)\\s+" + // job name ignored - "(\\S+)\\s+" + // job number - "(\\S+)\\s+" + // owner ignored - "(\\S+)\\s+" + // job status (OUTPUT,INPUT,ACTIVE) ignored - "(\\S+)\\s+" + // job class ignored - "(\\S+).*" // rest ignored - ; - - /* - * --------------------------------------------------------------------- - * Very brief and incomplete description of the zOS/MVS-filesystem. (Note: - * "zOS" is the operating system on the mainframe, and is the new name for - * MVS) - * - * The filesystem on the mainframe does not have hierarchal structure as for - * example the unix filesystem. For a more comprehensive description, please - * refer to the IBM manuals - * - * @LINK: - * http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/dgt2d440/CONTENTS - * - * - * Dataset names ============= - * - * A dataset name consist of a number of qualifiers separated by '.', each - * qualifier can be at most 8 characters, and the total length of a dataset - * can be max 44 characters including the dots. - * - * - * Dataset organisation ==================== - * - * A dataset represents a piece of storage allocated on one or more disks. - * The structure of the storage is described with the field dataset - * organinsation (DSORG). There are a number of dataset organisations, but - * only two are usable for FTP transfer. - * - * DSORG: PS: sequential, or flat file PO: partitioned dataset PO-E: - * extended partitioned dataset - * - * The PS file is just a flat file, as you would find it on the unix file - * system. - * - * The PO and PO-E files, can be compared to a single level directory - * structure. A PO file consist of a number of dataset members, or files if - * you will. It is possible to CD into the file, and to retrieve the - * individual members. - * - * - * Dataset record format ===================== - * - * The physical layout of the dataset is described on the dataset itself. - * There are a number of record formats (RECFM), but just a few is relavant - * for the FTP transfer. - * - * Any one beginning with either F or V can safely used by FTP transfer. All - * others should only be used with great care, so this version will just - * ignore the other record formats. F means a fixed number of records per - * allocated storage, and V means a variable number of records. - * - * - * Other notes =========== - * - * The file system supports automatically backup and retrieval of datasets. - * If a file is backed up, the ftp LIST command will return: ARCIVE Not - * Direct Access Device KJ.IOP998.ERROR.PL.UNITTEST - * - * - * Implementation notes ==================== - * - * Only datasets that have dsorg PS, PO or PO-E and have recfm beginning - * with F or V, is fully parsed. - * - * The following fields in FTPFile is used: FTPFile.Rawlisting: Always set. - * FTPFile.Type: DIRECTORY_TYPE or FILE_TYPE or UNKNOWN FTPFile.Name: name - * FTPFile.Timestamp: change time or null - * - * - * - * Additional information ====================== - * - * The MVS ftp server supports a number of features via the FTP interface. - * The features are controlled with the FTP command quote site filetype= - * SEQ is the default and used for normal file transfer JES is used to - * interact with the Job Entry Subsystem (JES) similar to a job scheduler - * DB2 is used to interact with a DB2 subsystem - * - * This parser supports SEQ and JES. - * - * - * - * - * - * - */ - - /** - * The sole constructor for a MVSFTPEntryParser object. - */ - public MVSFTPEntryParser() { - super(""); // note the regex is set in preParse. - super.configure(null); // configure parser with default configurations - } - - /** - * Parses a line of an z/OS - MVS FTP server file listing and converts it - * into a usable format in the form of an FTPFile instance. - * If the file listing line doesn't describe a file, then - * null is returned. Otherwise a FTPFile - * instance representing the file is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - boolean isParsed = false; - FTPFile f = new FTPFile(); - - if (isType == FILE_LIST_TYPE) { - isParsed = parseFileList(f, entry); - } else if (isType == MEMBER_LIST_TYPE) { - isParsed = parseMemberList(f, entry); - if (!isParsed) { - isParsed = parseSimpleEntry(f, entry); - } - } else if (isType == UNIX_LIST_TYPE) { - isParsed = parseUnixList(f, entry); - } else if (isType == JES_LEVEL_1_LIST_TYPE) { - isParsed = parseJeslevel1List(f, entry); - } else if (isType == JES_LEVEL_2_LIST_TYPE) { - isParsed = parseJeslevel2List(f, entry); - } - - if (!isParsed) { - f = null; - } - - return f; - } - - /** - * Parse entries representing a dataset list. Only datasets with DSORG PS or - * PO or PO-E and with RECFM F* or V* will be parsed. - * - * Format of ZOS/MVS file list: 1 2 3 4 5 6 7 8 9 10 Volume Unit Referred - * Ext Used Recfm Lrecl BlkSz Dsorg Dsname B10142 3390 2006/03/20 2 31 F 80 - * 80 PS MDI.OKL.WORK ARCIVE Not Direct Access Device - * KJ.IOP998.ERROR.PL.UNITTEST B1N231 3390 2006/03/20 1 15 VB 256 27998 PO - * PLU B1N231 3390 2006/03/20 1 15 VB 256 27998 PO-E PLB - * - * ----------------------------------- Group within Regex [1] Volume [2] - * Unit [3] Referred [4] Ext: number of extents [5] Used [6] Recfm: Record - * format [7] Lrecl: Logical record length [8] BlkSz: Block size [9] Dsorg: - * Dataset organisation. Many exists but only support: PS, PO, PO-E [10] - * Dsname: Dataset name - * - * Note: When volume is ARCIVE, it means the dataset is stored somewhere in - * a tape archive. These entries is currently not supported by this parser. - * A null value is returned. - * - * @param file will be updated with Name, Type, Timestamp if parsed. - * @param entry zosDirectoryEntry - * @return true: entry was parsed, false: entry was not parsed. - */ - private boolean parseFileList(FTPFile file, String entry) { - if (matches(entry)) { - file.setRawListing(entry); - String name = group(2); - String dsorg = group(1); - file.setName(name); - - // DSORG - if ("PS".equals(dsorg)) { - file.setType(FTPFile.FILE_TYPE); - } else if ("PO".equals(dsorg) || "PO-E".equals(dsorg)) { - // regex already ruled out anything other than PO or PO-E - file.setType(FTPFile.DIRECTORY_TYPE); - } else { - return false; - } - - return true; - } - - return false; - } - - /** - * Parse entries within a partitioned dataset. - * - * Format of a memberlist within a PDS: - *
-   *    0         1        2          3        4     5     6      7    8
-   *   Name      VV.MM   Created       Changed      Size  Init   Mod   Id
-   *   TBSHELF   01.03 2002/09/12 2002/10/11 09:37    11    11     0 KIL001
-   *   TBTOOL    01.12 2002/09/12 2004/11/26 19:54    51    28     0 KIL001
-   *
-   * -------------------------------------------
-   * [1] Name
-   * [2] VV.MM: Version . modification
-   * [3] Created: yyyy / MM / dd
-   * [4,5] Changed: yyyy / MM / dd HH:mm
-   * [6] Size: number of lines
-   * [7] Init: number of lines when first created
-   * [8] Mod: number of modified lines a last save
-   * [9] Id: User id for last update
-   * 
- * - * @param file will be updated with Name, Type and Timestamp if parsed. - * @param entry zosDirectoryEntry - * @return true: entry was parsed, false: entry was not parsed. - */ - private boolean parseMemberList(FTPFile file, String entry) { - if (matches(entry)) { - file.setRawListing(entry); - String name = group(1); - String datestr = group(2) + " " + group(3); - file.setName(name); - file.setType(FTPFile.FILE_TYPE); - try { - file.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - e.printStackTrace(); - // just ignore parsing errors. - // TODO check this is ok - return false; // this is a parsing failure too. - } - return true; - } - - return false; - } - - /** - * Assigns the name to the first word of the entry. Only to be used from a - * safe context, for example from a memberlist, where the regex for some - * reason fails. Then just assign the name field of FTPFile. - * - * @return true if the entry string is non-null and non-empty - */ - private boolean parseSimpleEntry(FTPFile file, String entry) { - if (entry != null && entry.trim().length() > 0) { - file.setRawListing(entry); - String name = entry.split(" ")[0]; - file.setName(name); - file.setType(FTPFile.FILE_TYPE); - return true; - } - return false; - } - - /** - * Parse the entry as a standard unix file. Using the UnixFTPEntryParser. - * - * @return true: entry is parsed, false: entry could not be parsed. - */ - private boolean parseUnixList(FTPFile file, String entry) { - file = unixFTPEntryParser.parseFTPEntry(entry); - if (file == null) { - return false; - } - return true; - } - - /** - * Matches these entries, note: no header: - *
-   * [1]      [2]      [3]   [4] [5]
-   * IBMUSER1 JOB01906 OUTPUT 3 Spool Files
-   * 012345678901234567890123456789012345678901234
-   *           1         2         3         4
-   * -------------------------------------------
-   * Group in regex
-   * [1] Job name
-   * [2] Job number
-   * [3] Job status (INPUT,ACTIVE,OUTPUT)
-   * [4] Number of sysout files
-   * [5] The string "Spool Files"
-   * 
- * - * @param file will be updated with Name, Type and Timestamp if parsed. - * @param entry zosDirectoryEntry - * @return true: entry was parsed, false: entry was not parsed. - */ - private boolean parseJeslevel1List(FTPFile file, String entry) { - if (matches(entry)) { - if (group(3).equalsIgnoreCase("OUTPUT")) { - file.setRawListing(entry); - String name = group(2); /* Job Number, used by GET */ - file.setName(name); - file.setType(FTPFile.FILE_TYPE); - return true; - } - } - - return false; - } - - /** - * Matches these entries: - *
-   * [1]      [2]      [3]     [4]    [5]
-   * JOBNAME  JOBID    OWNER   STATUS CLASS
-   * IBMUSER1 JOB01906 IBMUSER OUTPUT A       RC=0000 3 spool files
-   * IBMUSER  TSU01830 IBMUSER OUTPUT TSU     ABEND=522 3 spool files
-   * 012345678901234567890123456789012345678901234
-   *           1         2         3         4
-   * -------------------------------------------
-   * Group in regex
-   * [1] Job name
-   * [2] Job number
-   * [3] Owner
-   * [4] Job status (INPUT,ACTIVE,OUTPUT)
-   * [5] Job Class
-   * [6] The rest
-   * 
- * - * @param file will be updated with Name, Type and Timestamp if parsed. - * @param entry zosDirectoryEntry - * @return true: entry was parsed, false: entry was not parsed. - */ - private boolean parseJeslevel2List(FTPFile file, String entry) { - if (matches(entry)) { - if (group(4).equalsIgnoreCase("OUTPUT")) { - file.setRawListing(entry); - String name = group(2); /* Job Number, used by GET */ - file.setName(name); - file.setType(FTPFile.FILE_TYPE); - return true; - } - } - - return false; - } - - /** - * preParse is called as part of the interface. Per definition is is called - * before the parsing takes place. - * Three kind of lists is recognize: - * z/OS-MVS File lists - * z/OS-MVS Member lists - * unix file lists - * - * @since 2.0 - */ - @Override public List preParse(List orig) { - // simply remove the header line. Composite logic will take care of the - // two different types of - // list in short order. - if (orig != null && orig.size() > 0) { - String header = orig.get(0); - if (header.indexOf("Volume") >= 0 && header.indexOf("Dsname") >= 0) { - setType(FILE_LIST_TYPE); - super.setRegex(FILE_LIST_REGEX); - } else if (header.indexOf("Name") >= 0 && header.indexOf("Id") >= 0) { - setType(MEMBER_LIST_TYPE); - super.setRegex(MEMBER_LIST_REGEX); - } else if (header.indexOf("total") == 0) { - setType(UNIX_LIST_TYPE); - unixFTPEntryParser = new UnixFTPEntryParser(); - } else if (header.indexOf("Spool Files") >= 30) { - setType(JES_LEVEL_1_LIST_TYPE); - super.setRegex(JES_LEVEL_1_LIST_REGEX); - } else if (header.indexOf("JOBNAME") == 0 - && header.indexOf("JOBID") > 8) {// header contains JOBNAME JOBID OWNER // STATUS CLASS - setType(JES_LEVEL_2_LIST_TYPE); - super.setRegex(JES_LEVEL_2_LIST_REGEX); - } else { - setType(UNKNOWN_LIST_TYPE); - } - - if (isType != JES_LEVEL_1_LIST_TYPE) { // remove header is necessary - orig.remove(0); - } - } - - return orig; - } - - /** - * Explicitly set the type of listing being processed. - * - * @param type The listing type. - */ - void setType(int type) { - isType = type; - } - - /* - * @return - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_MVS, DEFAULT_DATE_FORMAT, null); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java deleted file mode 100644 index 2c8414d6..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/MacOsPeterFTPEntryParser.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; - -/** - * Implementation FTPFileEntryParser and FTPFileListParser for pre MacOS-X Systems. - * - * @version $Id: MacOsPeterFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - * @since 3.1 - */ -public class -MacOsPeterFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; //Nov 9 2001 - - static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; //Nov 9 20:06 - - /** - * this is the regular expression used by this parser. - * - * Permissions: - * r the file is readable - * w the file is writable - * x the file is executable - * - the indicated permission is not granted - * L mandatory locking occurs during access (the set-group-ID bit is - * on and the group execution bit is off) - * s the set-user-ID or set-group-ID bit is on, and the corresponding - * user or group execution bit is also on - * S undefined bit-state (the set-user-ID bit is on and the user - * execution bit is off) - * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and - * execution is on - * T the 1000 bit is turned on, and execution is off (undefined bit- - * state) - * e z/OS external link bit - */ - private static final String REGEX = "([bcdelfmpSs-])" - // type (1) - + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+" - // permission - + "(" - + "(folder\\s+)" - + "|" - + "((\\d+)\\s+(\\d+)\\s+)" - // resource size & data size - + ")" - + "(\\d+)\\s+" - // size - /* - * numeric or standard format date: - * yyyy-mm-dd (expecting hh:mm to follow) - * MMM [d]d - * [d]d MMM - * N.B. use non-space for MMM to allow for languages such as German which use - * diacritics (e.g. umlaut) in some abbreviations. - */ - + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3}))\\s+" - /* - year (for non-recent standard format) - yyyy - or time (for numeric or recent standard format) [h]h:mm - */ - + "(\\d+(?::\\d+)?)\\s+" - - + "(\\S*)(\\s*.*)"; // the rest - - /** - * The default constructor for a UnixFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public MacOsPeterFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of a UnixFTPEntryParser object with - * something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public MacOsPeterFTPEntryParser(FTPClientConfig config) { - super(REGEX); - configure(config); - } - - /** - * Parses a line of a unix (standard) FTP server file listing and converts - * it into a usable format in the form of an FTPFile - * instance. If the file listing line doesn't describe a file, - * null is returned, otherwise a FTPFile - * instance representing the files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - FTPFile file = new FTPFile(); - file.setRawListing(entry); - int type; - boolean isDevice = false; - - if (matches(entry)) { - String typeStr = group(1); - String hardLinkCount = "0"; - String usr = null; - String grp = null; - String filesize = group(20); - String datestr = group(21) + " " + group(22); - String name = group(23); - String endtoken = group(24); - - try { - file.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // intentionally do nothing - } - - // A 'whiteout' file is an ARTIFICIAL entry in any of several types of - // 'translucent' filesystems, of which a 'union' filesystem is one. - - // bcdelfmpSs- - switch (typeStr.charAt(0)) { - case 'd': - type = FTPFile.DIRECTORY_TYPE; - break; - case 'e': // NET-39 => z/OS external link - type = FTPFile.SYMBOLIC_LINK_TYPE; - break; - case 'l': - type = FTPFile.SYMBOLIC_LINK_TYPE; - break; - case 'b': - case 'c': - isDevice = true; - type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented - break; - case 'f': - case '-': - type = FTPFile.FILE_TYPE; - break; - default: // e.g. ? and w = whiteout - type = FTPFile.UNKNOWN_TYPE; - } - - file.setType(type); - - int g = 4; - for (int access = 0; access < 3; access++, g += 4) { - // Use != '-' to avoid having to check for suid and sticky bits - file.setPermission(access, FTPFile.READ_PERMISSION, (!group(g).equals("-"))); - file.setPermission(access, FTPFile.WRITE_PERMISSION, (!group(g + 1).equals("-"))); - - String execPerm = group(g + 2); - if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) { - file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); - } else { - file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); - } - } - - if (!isDevice) { - try { - file.setHardLinkCount(Integer.parseInt(hardLinkCount)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - } - - file.setUser(usr); - file.setGroup(grp); - - try { - file.setSize(Long.parseLong(filesize)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - - if (null == endtoken) { - file.setName(name); - } else { - // oddball cases like symbolic links, file names - // with spaces in them. - name += endtoken; - if (type == FTPFile.SYMBOLIC_LINK_TYPE) { - - int end = name.indexOf(" -> "); - // Give up if no link indicator is present - if (end == -1) { - file.setName(name); - } else { - file.setName(name.substring(0, end)); - file.setLink(name.substring(end + 4)); - } - } else { - file.setName(name); - } - } - return file; - } - return null; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, - DEFAULT_RECENT_DATE_FORMAT); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java deleted file mode 100644 index cee2811a..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NTFTPEntryParser.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.Configurable; -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; -import java.util.regex.Pattern; - -/** - * Implementation of FTPFileEntryParser and FTPFileListParser for NT Systems. - * - * @version $Id: NTFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - */ -public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy hh:mma"; //11-09-01 12:30PM - - private static final String DEFAULT_DATE_FORMAT2 = "MM-dd-yy kk:mm"; //11-09-01 18:30 - - private final FTPTimestampParser timestampParser; - - /** - * this is the regular expression used by this parser. - */ - private static final String REGEX = - "(\\S+)\\s+(\\S+)\\s+" // MM-dd-yy whitespace hh:mma|kk:mm; swallow trailing spaces - + "(?:()|([0-9]+))\\s+" // or ddddd; swallow trailing spaces - + "(\\S.*)"; // First non-space followed by rest of line (name) - - /** - * The sole constructor for an NTFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public NTFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of an NTFTPEntryParser object - * with something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public NTFTPEntryParser(FTPClientConfig config) { - super(REGEX, Pattern.DOTALL); - configure(config); - FTPClientConfig config2 = - new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT2, null); - config2.setDefaultDateFormatStr(DEFAULT_DATE_FORMAT2); - this.timestampParser = new FTPTimestampParserImpl(); - ((Configurable) this.timestampParser).configure(config2); - } - - /** - * Parses a line of an NT FTP server file listing and converts it into a - * usable format in the form of an FTPFile instance. If the - * file listing line doesn't describe a file, null is - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - FTPFile f = new FTPFile(); - f.setRawListing(entry); - - if (matches(entry)) { - String datestr = group(1) + " " + group(2); - String dirString = group(3); - String size = group(4); - String name = group(5); - try { - f.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // parsing fails, try the other date format - try { - f.setTimestamp(timestampParser.parseTimestamp(datestr)); - } catch (ParseException e2) { - // intentionally do nothing - } - } - - if (null == name || name.equals(".") || name.equals("..")) { - return (null); - } - f.setName(name); - - if ("".equals(dirString)) { - f.setType(FTPFile.DIRECTORY_TYPE); - f.setSize(0); - } else { - f.setType(FTPFile.FILE_TYPE); - if (null != size) { - f.setSize(Long.parseLong(size)); - } - } - return (f); - } - return null; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override public FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_NT, DEFAULT_DATE_FORMAT, null); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java deleted file mode 100644 index 7585ebbe..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; - -/** - * Implementation of FTPFileEntryParser and FTPFileListParser for Netware Systems. Note that some of - * the proprietary - * extensions for Novell-specific operations are not supported. See - * - * http://www.novell.com/documentation/nw65/index.html?page=/documentation/nw65/ftp_enu/data/fbhbgcfa.html - * for more details. - * - * @version $Id: NetwareFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - * @since 1.5 - */ -public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - /** - * Default date format is e.g. Feb 22 2006 - */ - private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy"; - - /** - * Default recent date format is e.g. Feb 22 17:32 - */ - private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm"; - - /** - * this is the regular expression used by this parser. - * Example: d [-W---F--] SCION_VOL2 512 Apr 13 23:12 VOL2 - */ - private static final String REGEX = "(d|-){1}\\s+" // Directory/file flag - + "\\[([-A-Z]+)\\]\\s+" // Attributes RWCEAFMS or - - + "(\\S+)\\s+" + "(\\d+)\\s+" // Owner and size - + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format - + "\\s+(.*)"; // Filename (incl. spaces) - - /** - * The default constructor for a NetwareFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public NetwareFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of an NetwareFTPEntryParser object - * with something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public NetwareFTPEntryParser(FTPClientConfig config) { - super(REGEX); - configure(config); - } - - /** - * Parses a line of an NetwareFTP server file listing and converts it into a - * usable format in the form of an FTPFile instance. If the - * file listing line doesn't describe a file, null is - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - *

- * Netware file permissions are in the following format: RWCEAFMS, and are explained as follows: - *

    - *
  • S - Supervisor; All rights. - *
  • R - Read; Right to open and read or execute. - *
  • W - Write; Right to open and modify. - *
  • C - Create; Right to create; when assigned to a file, allows a deleted file to be - * recovered. - *
  • E - Erase; Right to delete. - *
  • M - Modify; Right to rename a file and to change attributes. - *
  • F - File Scan; Right to see directory or file listings. - *
  • A - Access Control; Right to modify trustee assignments and the Inherited Rights - * Mask. - *
- * - * See - * - * here - * for more details - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - - FTPFile f = new FTPFile(); - if (matches(entry)) { - String dirString = group(1); - String attrib = group(2); - String user = group(3); - String size = group(4); - String datestr = group(5); - String name = group(9); - - try { - f.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // intentionally do nothing - } - - //is it a DIR or a file - if (dirString.trim().equals("d")) { - f.setType(FTPFile.DIRECTORY_TYPE); - } else // Should be "-" - { - f.setType(FTPFile.FILE_TYPE); - } - - f.setUser(user); - - //set the name - f.setName(name.trim()); - - //set the size - f.setSize(Long.parseLong(size.trim())); - - // Now set the permissions (or at least a subset thereof - full permissions would probably require - // subclassing FTPFile and adding extra metainformation there) - if (attrib.indexOf("R") != -1) { - f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true); - } - if (attrib.indexOf("W") != -1) { - f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true); - } - - return (f); - } - return null; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_NETWARE, DEFAULT_DATE_FORMAT, - DEFAULT_RECENT_DATE_FORMAT); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java deleted file mode 100644 index fc2413e9..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS2FTPEntryParser.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; - -/** - * Implementation of FTPFileEntryParser and FTPFileListParser for OS2 Systems. - * - * @version $Id: OS2FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - */ -public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl - -{ - - private static final String DEFAULT_DATE_FORMAT = "MM-dd-yy HH:mm"; //11-09-01 12:30 - /** - * this is the regular expression used by this parser. - */ - private static final String REGEX = "\\s*([0-9]+)\\s*" - + "(\\s+|[A-Z]+)\\s*" - + "(DIR|\\s+)\\s*" - + "(\\S+)\\s+(\\S+)\\s+" /* date stuff */ - + "(\\S.*)"; - - /** - * The default constructor for a OS2FTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public OS2FTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of an OS2FTPEntryParser object - * with something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public OS2FTPEntryParser(FTPClientConfig config) { - super(REGEX); - configure(config); - } - - /** - * Parses a line of an OS2 FTP server file listing and converts it into a - * usable format in the form of an FTPFile instance. If the - * file listing line doesn't describe a file, null is - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - - FTPFile f = new FTPFile(); - if (matches(entry)) { - String size = group(1); - String attrib = group(2); - String dirString = group(3); - String datestr = group(4) + " " + group(5); - String name = group(6); - try { - f.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // intentionally do nothing - } - - //is it a DIR or a file - if (dirString.trim().equals("DIR") || attrib.trim().equals("DIR")) { - f.setType(FTPFile.DIRECTORY_TYPE); - } else { - f.setType(FTPFile.FILE_TYPE); - } - - //set the name - f.setName(name.trim()); - - //set the size - f.setSize(Long.parseLong(size.trim())); - - return (f); - } - return null; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_OS2, DEFAULT_DATE_FORMAT, null); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java deleted file mode 100644 index 43198267..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/OS400FTPEntryParser.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import java.io.File; -import java.text.ParseException; - -/** - * @version $Id: OS400FTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - *
- *          Example *FILE/*MEM FTP entries, when the current
- *          working directory is a file of file system QSYS:
- *          ------------------------------------------------
- *
- *          $ cwd /qsys.lib/rpgunit.lib/rpgunitc1.file
- *            250-NAMEFMT set to 1.
- *            250 "/QSYS.LIB/RPGUNIT.LIB/RPGUNITC1.FILE" is current directory.
- *          $ dir
- *            227 Entering Passive Mode (10,200,36,33,40,249).
- *            125 List started.
- *            QPGMR          135168 22.06.13 13:18:19 *FILE
- *            QPGMR                                   *MEM       MKCMD.MBR
- *            QPGMR                                   *MEM       RUCALLTST.MBR
- *            QPGMR                                   *MEM       RUCMDHLP.MBR
- *            QPGMR                                   *MEM       RUCRTTST.MBR
- *            250 List completed.
- *
- *
- *          Example *FILE entry of an OS/400 save file:
- *          ---------------------------------------------------
- *
- *          $ cwd /qsys.lib/rpgunit.lib
- *            250 "/QSYS.LIB/RPGUNIT.LIB" is current library.
- *          $ dir rpgunit.file
- *            227 Entering Passive Mode (10,200,36,33,188,106).
- *            125 List started.
- *            QPGMR        16347136 29.06.13 15:45:09 *FILE      RPGUNIT.SAVF
- *            250 List completed.
- *
- *
- *          Example *STMF/*DIR FTP entries, when the
- *          current working directory is in file system "root":
- *          ---------------------------------------------------
- *
- *          $ cwd /home/raddatz
- *            250 "/home/raddatz" is current directory.
- *          $ dir test*
- *            227 Entering Passive Mode (10,200,36,33,200,189).
- *            125 List started.
- *            RADDATZ           200 21.05.11 12:31:18 *STMF      TEST_RG_02_CRLF.properties
- *            RADDATZ           187 08.05.11 12:31:40 *STMF      TEST_RG_02_LF.properties
- *            RADDATZ           187 08.05.11 12:31:52 *STMF      TEST_RG_02_CR.properties
- *            RADDATZ          8192 04.07.13 09:04:14 *DIR       testDir1/
- *            RADDATZ          8192 04.07.13 09:04:17 *DIR       testDir2/
- *            250 List completed.
- *
- *
- *          Example 1, using ANT to list specific members of a file:
- *          --------------------------------------------------------
- *
- *               <echo/>
- *               <echo>Listing members of a file:</echo>
- *
- *               <ftp action="list"
- *                    server="${ftp.server}"
- *                    userid="${ftp.user}"
- *                    password="${ftp.password}"
- *                    binary="false"
- *                    verbose="true"
- *                    remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
- *                    systemTypeKey="OS/400"
- *                    listing="ftp-listing.txt"
- *                    >
- *                   <fileset dir="./i5-downloads-file" casesensitive="false">
- *                       <include name="run*.mbr" />
- *                   </fileset>
- *               </ftp>
- *
- *          Output:
- *          -------
- *
- *            [echo] Listing members of a file:
- *             [ftp] listing files
- *             [ftp] listing RUN.MBR
- *             [ftp] listing RUNNER.MBR
- *             [ftp] listing RUNNERBND.MBR
- *             [ftp] 3 files listed
- *
- *
- *          Example 2, using ANT to list specific members of all files of a library:
- *          ------------------------------------------------------------------------
- *
- *               <echo/>
- *               <echo>Listing members of all files of a library:</echo>
- *
- *               <ftp action="list"
- *                    server="${ftp.server}"
- *                    userid="${ftp.user}"
- *                    password="${ftp.password}"
- *                    binary="false"
- *                    verbose="true"
- *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
- *                    systemTypeKey="OS/400"
- *                    listing="ftp-listing.txt"
- *                    >
- *                   <fileset dir="./i5-downloads-lib" casesensitive="false">
- *                       <include name="**\run*.mbr" />
- *                   </fileset>
- *               </ftp>
- *
- *          Output:
- *          -------
- *
- *            [echo] Listing members of all files of a library:
- *             [ftp] listing files
- *             [ftp] listing RPGUNIT1.FILE\RUN.MBR
- *             [ftp] listing RPGUNIT1.FILE\RUNRMT.MBR
- *             [ftp] listing RPGUNITT1.FILE\RUNT.MBR
- *             [ftp] listing RPGUNITY1.FILE\RUN.MBR
- *             [ftp] listing RPGUNITY1.FILE\RUNNER.MBR
- *             [ftp] listing RPGUNITY1.FILE\RUNNERBND.MBR
- *             [ftp] 6 files listed
- *
- *
- *          Example 3, using ANT to download specific members of a file:
- *          ------------------------------------------------------------
- *
- *               <echo/>
- *               <echo>Downloading members of a file:</echo>
- *
- *               <ftp action="get"
- *                    server="${ftp.server}"
- *                    userid="${ftp.user}"
- *                    password="${ftp.password}"
- *                    binary="false"
- *                    verbose="true"
- *                    remotedir="/QSYS.LIB/RPGUNIT.LIB/RPGUNITY1.FILE"
- *                    systemTypeKey="OS/400"
- *                    >
- *                   <fileset dir="./i5-downloads-file" casesensitive="false">
- *                       <include name="run*.mbr" />
- *                   </fileset>
- *               </ftp>
- *
- *          Output:
- *          -------
- *
- *            [echo] Downloading members of a file:
- *             [ftp] getting files
- *             [ftp] transferring RUN.MBR to C:\workspaces\rdp_080\workspace\ANT -
- *          FTP\i5-downloads-file\RUN.MBR
- *             [ftp] transferring RUNNER.MBR to C:\workspaces\rdp_080\workspace\ANT -
- *          FTP\i5-downloads-file\RUNNER.MBR
- *             [ftp] transferring RUNNERBND.MBR to C:\workspaces\rdp_080\workspace\ANT -
- *          FTP\i5-downloads-file\RUNNERBND.MBR
- *             [ftp] 3 files retrieved
- *
- *
- *          Example 4, using ANT to download specific members of all files of a library:
- *          ----------------------------------------------------------------------------
- *
- *               <echo/>
- *               <echo>Downloading members of all files of a library:</echo>
- *
- *               <ftp action="get"
- *                    server="${ftp.server}"
- *                    userid="${ftp.user}"
- *                    password="${ftp.password}"
- *                    binary="false"
- *                    verbose="true"
- *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
- *                    systemTypeKey="OS/400"
- *                    >
- *                   <fileset dir="./i5-downloads-lib" casesensitive="false">
- *                       <include name="**\run*.mbr" />
- *                   </fileset>
- *               </ftp>
- *
- *          Output:
- *          -------
- *
- *            [echo] Downloading members of all files of a library:
- *             [ftp] getting files
- *             [ftp] transferring RPGUNIT1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNIT1.FILE\RUN.MBR
- *             [ftp] transferring RPGUNIT1.FILE\RUNRMT.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNIT1.FILE\RUNRMT.MBR
- *             [ftp] transferring RPGUNITT1.FILE\RUNT.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNITT1.FILE\RUNT.MBR
- *             [ftp] transferring RPGUNITY1.FILE\RUN.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNITY1.FILE\RUN.MBR
- *             [ftp] transferring RPGUNITY1.FILE\RUNNER.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNITY1.FILE\RUNNER.MBR
- *             [ftp] transferring RPGUNITY1.FILE\RUNNERBND.MBR to C:\work\rdp_080\space\ANT -
- *          FTP\i5-downloads\RPGUNITY1.FILE\RUNNERBND.MBR
- *             [ftp] 6 files retrieved
- *
- *
- *          Example 5, using ANT to download a save file of a library:
- *          ----------------------------------------------------------
- *
- *               <ftp action="get"
- *                    server="${ftp.server}"
- *                    userid="${ftp.user}"
- *                    password="${ftp.password}"
- *                    binary="true"
- *                    verbose="true"
- *                    remotedir="/QSYS.LIB/RPGUNIT.LIB"
- *                    systemTypeKey="OS/400"
- *                    >
- *                 <fileset dir="./i5-downloads-savf" casesensitive="false">
- *                     <include name="RPGUNIT.SAVF" />
- *                 </fileset>
- *               </ftp>
- *
- *          Output:
- *          -------
- *            [echo] Downloading save file:
- *             [ftp] getting files
- *             [ftp] transferring RPGUNIT.SAVF to C:\workspaces\rdp_080\workspace\net-Test\i5-downloads-lib\RPGUNIT.SAVF
- *             [ftp] 1 files retrieved
- *
- *          
- */ -public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - private static final String DEFAULT_DATE_FORMAT = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24 - - private static final String REGEX = "(\\S+)\\s+" // user - + "(?:(\\d+)\\s+)?" // size, empty for members - + "(?:(\\S+)\\s+(\\S+)\\s+)?" // date stuff, empty for members - + "(\\*STMF|\\*DIR|\\*FILE|\\*MEM)\\s+" // *STMF/*DIR/*FILE/*MEM - + "(?:(\\S+)\\s*)?"; // filename, missing, when CWD is a *FILE - - /** - * The default constructor for a OS400FTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public OS400FTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of an OS400FTPEntryParser object - * with something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public OS400FTPEntryParser(FTPClientConfig config) { - super(REGEX); - configure(config); - } - - @Override public FTPFile parseFTPEntry(String entry) { - - FTPFile file = new FTPFile(); - file.setRawListing(entry); - int type; - - if (matches(entry)) { - String usr = group(1); - String filesize = group(2); - String datestr = ""; - if (!isNullOrEmpty(group(3)) || !isNullOrEmpty(group(4))) { - datestr = group(3) + " " + group(4); - } - String typeStr = group(5); - String name = group(6); - - boolean mustScanForPathSeparator = true; - - try { - file.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // intentionally do nothing - } - - if (typeStr.equalsIgnoreCase("*STMF")) { - type = FTPFile.FILE_TYPE; - if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { - return null; - } - } else if (typeStr.equalsIgnoreCase("*DIR")) { - type = FTPFile.DIRECTORY_TYPE; - if (isNullOrEmpty(filesize) || isNullOrEmpty(name)) { - return null; - } - } else if (typeStr.equalsIgnoreCase("*FILE")) { - // File, defines the structure of the data (columns of a row) - // but the data is stored in one or more members. Typically a - // source file contains multiple members whereas it is - // recommended (but not enforced) to use one member per data - // file. - // Save files are a special type of files which are used - // to save objects, e.g. for backups. - if (name != null && name.toUpperCase().endsWith(".SAVF")) { - mustScanForPathSeparator = false; - type = FTPFile.FILE_TYPE; - } else { - return null; - } - } else if (typeStr.equalsIgnoreCase("*MEM")) { - mustScanForPathSeparator = false; - type = FTPFile.FILE_TYPE; - - if (isNullOrEmpty(name)) { - return null; - } - if (!(isNullOrEmpty(filesize) && isNullOrEmpty(datestr))) { - return null; - } - - // Quick and dirty bug fix to make SelectorUtils work. - // Class SelectorUtils uses 'File.separator' to splitt - // a given path into pieces. But actually it had to - // use the separator of the FTP server, which is a forward - // slash in case of an AS/400. - name = name.replace('/', File.separatorChar); - } else { - type = FTPFile.UNKNOWN_TYPE; - } - - file.setType(type); - - file.setUser(usr); - - try { - file.setSize(Long.parseLong(filesize)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - - if (name.endsWith("/")) { - name = name.substring(0, name.length() - 1); - } - if (mustScanForPathSeparator) { - int pos = name.lastIndexOf('/'); - if (pos > -1) { - name = name.substring(pos + 1); - } - } - - file.setName(name); - - return file; - } - return null; - } - - /** - * @param string String value that is checked for null - * or empty. - * @return true for null or empty values, - * else false. - */ - private boolean isNullOrEmpty(String string) { - if (string == null || string.length() == 0) { - return true; - } - return false; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_OS400, DEFAULT_DATE_FORMAT, null); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java deleted file mode 100644 index 72576687..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/ParserInitializationException.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -/** - * This class encapsulates all errors that may be thrown by - * the process of an FTPFileEntryParserFactory creating and - * instantiating an FTPFileEntryParser. - */ -public class ParserInitializationException extends RuntimeException { - - private static final long serialVersionUID = 5563335279583210658L; - - /** - * Constucts a ParserInitializationException with just a message - * - * @param message Exception message - */ - public ParserInitializationException(String message) { - super(message); - } - - /** - * Constucts a ParserInitializationException with a message - * and a root cause. - * - * @param message Exception message - * @param rootCause root cause throwable that caused - * this to be thrown - */ - public ParserInitializationException(String message, Throwable rootCause) { - super(message, rootCause); - } - - /** - * returns the root cause of this exception or null - * if no root cause was specified. - * - * @return the root cause of this exception being thrown - * @deprecated use {@link #getCause()} instead - */ - @Deprecated public Throwable getRootCause() { - return super.getCause(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java deleted file mode 100644 index 90100f89..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/RegexFTPFileEntryParserImpl.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPFileEntryParserImpl; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * This abstract class implements both the older FTPFileListParser and - * newer FTPFileEntryParser interfaces with default functionality. - * All the classes in the parser subpackage inherit from this. - * - * This is the base class for all regular expression based FTPFileEntryParser classes - */ -public abstract class RegexFTPFileEntryParserImpl extends FTPFileEntryParserImpl { - /** - * internal pattern the matcher tries to match, representing a file - * entry - */ - private Pattern pattern = null; - - /** - * internal match result used by the parser - */ - private MatchResult result = null; - - /** - * Internal PatternMatcher object used by the parser. It has protected - * scope in case subclasses want to make use of it for their own purposes. - */ - protected Matcher _matcher_ = null; - - /** - * The constructor for a RegexFTPFileEntryParserImpl object. - * The expression is compiled with flags = 0. - * - * @param regex The regular expression with which this object is - * initialized. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen in - * normal conditions. It it is seen, this is a sign that a subclass has - * been created with a bad regular expression. Since the parser must be - * created before use, this means that any bad parser subclasses created - * from this will bomb very quickly, leading to easy detection. - */ - - public RegexFTPFileEntryParserImpl(String regex) { - super(); - compileRegex(regex, 0); - } - - /** - * The constructor for a RegexFTPFileEntryParserImpl object. - * - * @param regex The regular expression with which this object is - * initialized. - * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen in - * normal conditions. It it is seen, this is a sign that a subclass has - * been created with a bad regular expression. Since the parser must be - * created before use, this means that any bad parser subclasses created - * from this will bomb very quickly, leading to easy detection. - * @since 3.4 - */ - public RegexFTPFileEntryParserImpl(String regex, final int flags) { - super(); - compileRegex(regex, flags); - } - - /** - * Convenience method delegates to the internal MatchResult's matches() - * method. - * - * @param s the String to be matched - * @return true if s matches this object's regular expression. - */ - - public boolean matches(String s) { - this.result = null; - _matcher_ = pattern.matcher(s); - if (_matcher_.matches()) { - this.result = _matcher_.toMatchResult(); - } - return null != this.result; - } - - /** - * Convenience method - * - * @return the number of groups() in the internal MatchResult. - */ - - public int getGroupCnt() { - if (this.result == null) { - return 0; - } - return this.result.groupCount(); - } - - /** - * Convenience method delegates to the internal MatchResult's group() - * method. - * - * @param matchnum match group number to be retrieved - * @return the content of the matchnum'th group of the internal - * match or null if this method is called without a match having - * been made. - */ - public String group(int matchnum) { - if (this.result == null) { - return null; - } - return this.result.group(matchnum); - } - - /** - * For debugging purposes - returns a string shows each match group by - * number. - * - * @return a string shows each match group by number. - */ - - public String getGroupsAsString() { - StringBuilder b = new StringBuilder(); - for (int i = 1; i <= this.result.groupCount(); i++) { - b.append(i) - .append(") ") - .append(this.result.group(i)) - .append(System.getProperty("line.separator")); - } - return b.toString(); - } - - /** - * Alter the current regular expression being utilised for entry parsing - * and create a new {@link Pattern} instance. - * - * @param regex The new regular expression - * @return true - * @throws IllegalArgumentException if the regex cannot be compiled - * @since 2.0 - */ - public boolean setRegex(final String regex) { - compileRegex(regex, 0); - return true; - } - - /** - * Alter the current regular expression being utilised for entry parsing - * and create a new {@link Pattern} instance. - * - * @param regex The new regular expression - * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. - * @return true - * @throws IllegalArgumentException if the regex cannot be compiled - * @since 3.4 - */ - public boolean setRegex(final String regex, final int flags) { - compileRegex(regex, flags); - return true; - } - - /** - * Compile the regex and store the {@link Pattern}. - * - * This is an internal method to do the work so the constructor does not - * have to call an overrideable method. - * - * @param regex the expression to compile - * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none. - * @throws IllegalArgumentException if the regex cannot be compiled - */ - private void compileRegex(final String regex, final int flags) { - try { - pattern = Pattern.compile(regex, flags); - } catch (PatternSyntaxException pse) { - throw new IllegalArgumentException("Unparseable regex supplied: " + regex); - } - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java deleted file mode 100644 index 5e35f308..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/UnixFTPEntryParser.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.text.ParseException; -import java.util.List; -import java.util.ListIterator; - -/** - * Implementation FTPFileEntryParser and FTPFileListParser for standard - * Unix Systems. - * - * This class is based on the logic of Daniel Savarese's - * DefaultFTPListParser, but adapted to use regular expressions and to fit the - * new FTPFileEntryParser interface. - * - * @version $Id: UnixFTPEntryParser.java 1781925 2017-02-06 16:43:40Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - */ -public class UnixFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - static final String DEFAULT_DATE_FORMAT = "MMM d yyyy"; //Nov 9 2001 - - static final String DEFAULT_RECENT_DATE_FORMAT = "MMM d HH:mm"; //Nov 9 20:06 - - static final String NUMERIC_DATE_FORMAT = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06 - - // Suffixes used in Japanese listings after the numeric values - private static final String JA_MONTH = "\u6708"; - private static final String JA_DAY = "\u65e5"; - private static final String JA_YEAR = "\u5e74"; - - private static final String DEFAULT_DATE_FORMAT_JA = - "M'" + JA_MONTH + "' d'" + JA_DAY + "' yyyy'" + JA_YEAR + "'"; //6月 3日 2003年 - - private static final String DEFAULT_RECENT_DATE_FORMAT_JA = - "M'" + JA_MONTH + "' d'" + JA_DAY + "' HH:mm"; //8月 17日 20:10 - - /** - * Some Linux distributions are now shipping an FTP server which formats - * file listing dates in an all-numeric format: - * "yyyy-MM-dd HH:mm. - * This is a very welcome development, and hopefully it will soon become - * the standard. However, since it is so new, for now, and possibly - * forever, we merely accomodate it, but do not make it the default. - *

- * For now end users may specify this format only via - * UnixFTPEntryParser(FTPClientConfig). - * Steve Cohen - 2005-04-17 - */ - public static final FTPClientConfig NUMERIC_DATE_CONFIG = - new FTPClientConfig(FTPClientConfig.SYST_UNIX, NUMERIC_DATE_FORMAT, null); - - /** - * this is the regular expression used by this parser. - * - * Permissions: - * r the file is readable - * w the file is writable - * x the file is executable - * - the indicated permission is not granted - * L mandatory locking occurs during access (the set-group-ID bit is - * on and the group execution bit is off) - * s the set-user-ID or set-group-ID bit is on, and the corresponding - * user or group execution bit is also on - * S undefined bit-state (the set-user-ID bit is on and the user - * execution bit is off) - * t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and - * execution is on - * T the 1000 bit is turned on, and execution is off (undefined bit- - * state) - * e z/OS external link bit - * Final letter may be appended: - * + file has extended security attributes (e.g. ACL) - * Note: local listings on MacOSX also use '@'; - * this is not allowed for here as does not appear to be shown by FTP servers - * {@code @} file has extended attributes - */ - private static final String REGEX = "([bcdelfmpSs-])" // file type - + "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions - - + "\\s*" // separator TODO why allow it to be omitted?? - - + "(\\d+)" // link count - - + "\\s+" // separator - - + "(?:(\\S+(?:\\s\\S+)*?)\\s+)?" // owner name (optional spaces) - + "(?:(\\S+(?:\\s\\S+)*)\\s+)?" // group name (optional spaces) - + "(\\d+(?:,\\s*\\d+)?)" // size or n,m - - + "\\s+" // separator - - /* - * numeric or standard format date: - * yyyy-mm-dd (expecting hh:mm to follow) - * MMM [d]d - * [d]d MMM - * N.B. use non-space for MMM to allow for languages such as German which use - * diacritics (e.g. umlaut) in some abbreviations. - * Japanese uses numeric day and month with suffixes to distinguish them - * [d]dXX [d]dZZ - */ + "(" + "(?:\\d+[-/]\\d+[-/]\\d+)" + // yyyy-mm-dd - "|(?:\\S{3}\\s+\\d{1,2})" + // MMM [d]d - "|(?:\\d{1,2}\\s+\\S{3})" + // [d]d MMM - "|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + ")" + ")" - - + "\\s+" // separator - - /* - year (for non-recent standard format) - yyyy - or time (for numeric or recent standard format) [h]h:mm - or Japanese year - yyyyXX - */ + "((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))" // (20) - - + "\\s" // separator - - + "(.*)"; // the rest (21) - - // if true, leading spaces are trimmed from file names - // this was the case for the original implementation - final boolean trimLeadingSpaces; // package protected for access from test code - - /** - * The default constructor for a UnixFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public UnixFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of a UnixFTPEntryParser object with - * something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public UnixFTPEntryParser(FTPClientConfig config) { - this(config, false); - } - - /** - * This constructor allows the creation of a UnixFTPEntryParser object with - * something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @param trimLeadingSpaces if {@code true}, trim leading spaces from file names - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 3.4 - */ - public UnixFTPEntryParser(FTPClientConfig config, boolean trimLeadingSpaces) { - super(REGEX); - configure(config); - this.trimLeadingSpaces = trimLeadingSpaces; - } - - /** - * Preparse the list to discard "total nnn" lines - */ - @Override public List preParse(List original) { - ListIterator iter = original.listIterator(); - while (iter.hasNext()) { - String entry = iter.next(); - if (entry.matches("^total \\d+$")) { // NET-389 - iter.remove(); - } - } - return original; - } - - /** - * Parses a line of a unix (standard) FTP server file listing and converts - * it into a usable format in the form of an FTPFile - * instance. If the file listing line doesn't describe a file, - * null is returned, otherwise a FTPFile - * instance representing the files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - FTPFile file = new FTPFile(); - file.setRawListing(entry); - int type; - boolean isDevice = false; - - if (matches(entry)) { - String typeStr = group(1); - String hardLinkCount = group(15); - String usr = group(16); - String grp = group(17); - String filesize = group(18); - String datestr = group(19) + " " + group(20); - String name = group(21); - if (trimLeadingSpaces) { - name = name.replaceFirst("^\\s+", ""); - } - - try { - if (group(19).contains(JA_MONTH)) { // special processing for Japanese format - FTPTimestampParserImpl jaParser = new FTPTimestampParserImpl(); - jaParser.configure(new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT_JA, - DEFAULT_RECENT_DATE_FORMAT_JA)); - file.setTimestamp(jaParser.parseTimestamp(datestr)); - } else { - file.setTimestamp(super.parseTimestamp(datestr)); - } - } catch (ParseException e) { - // intentionally do nothing - } - - // A 'whiteout' file is an ARTIFICIAL entry in any of several types of - // 'translucent' filesystems, of which a 'union' filesystem is one. - - // bcdelfmpSs- - switch (typeStr.charAt(0)) { - case 'd': - type = FTPFile.DIRECTORY_TYPE; - break; - case 'e': // NET-39 => z/OS external link - type = FTPFile.SYMBOLIC_LINK_TYPE; - break; - case 'l': - type = FTPFile.SYMBOLIC_LINK_TYPE; - break; - case 'b': - case 'c': - isDevice = true; - type = FTPFile.FILE_TYPE; // TODO change this if DEVICE_TYPE implemented - break; - case 'f': - case '-': - type = FTPFile.FILE_TYPE; - break; - default: // e.g. ? and w = whiteout - type = FTPFile.UNKNOWN_TYPE; - } - - file.setType(type); - - int g = 4; - for (int access = 0; access < 3; access++, g += 4) { - // Use != '-' to avoid having to check for suid and sticky bits - file.setPermission(access, FTPFile.READ_PERMISSION, (!group(g).equals("-"))); - file.setPermission(access, FTPFile.WRITE_PERMISSION, (!group(g + 1).equals("-"))); - - String execPerm = group(g + 2); - if (!execPerm.equals("-") && !Character.isUpperCase(execPerm.charAt(0))) { - file.setPermission(access, FTPFile.EXECUTE_PERMISSION, true); - } else { - file.setPermission(access, FTPFile.EXECUTE_PERMISSION, false); - } - } - - if (!isDevice) { - try { - file.setHardLinkCount(Integer.parseInt(hardLinkCount)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - } - - file.setUser(usr); - file.setGroup(grp); - - try { - file.setSize(Long.parseLong(filesize)); - } catch (NumberFormatException e) { - // intentionally do nothing - } - - // oddball cases like symbolic links, file names - // with spaces in them. - if (type == FTPFile.SYMBOLIC_LINK_TYPE) { - - int end = name.indexOf(" -> "); - // Give up if no link indicator is present - if (end == -1) { - file.setName(name); - } else { - file.setName(name.substring(0, end)); - file.setLink(name.substring(end + 4)); - } - } else { - file.setName(name); - } - return file; - } - return null; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_UNIX, DEFAULT_DATE_FORMAT, - DEFAULT_RECENT_DATE_FORMAT); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java deleted file mode 100644 index b2578cd0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFile; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import aria.apache.commons.net.ftp.FTPListParseEngine; -import java.io.BufferedReader; -import java.io.IOException; -import java.text.ParseException; -import java.util.StringTokenizer; - -/** - * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems. - * This is a sample of VMS LIST output - * - * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - *

- * Note: VMSFTPEntryParser can only be instantiated through the - * DefaultFTPParserFactory by classname. It will not be chosen - * by the autodetection scheme. - * - *

- * - * @version $Id: VMSFTPEntryParser.java 1752660 2016-07-14 13:25:39Z sebb $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - * @see DefaultFTPFileEntryParserFactory - */ -public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { - - private static final String DEFAULT_DATE_FORMAT = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24 - - /** - * this is the regular expression used by this parser. - */ - private static final String REGEX = - "(.*?;[0-9]+)\\s*" //1 file and version - + "(\\d+)/\\d+\\s*" //2 size/allocated - + "(\\S+)\\s+(\\S+)\\s+" //3+4 date and time - + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*" //5(6,7,8) owner - + "\\([a-zA-Z]*,([a-zA-Z]*),([a-zA-Z]*),([a-zA-Z]*)\\)"; - //9,10,11 Permissions (O,G,W) - // TODO - perhaps restrict permissions to [RWED]* ? - - /** - * Constructor for a VMSFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public VMSFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of a VMSFTPEntryParser object with - * something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public VMSFTPEntryParser(FTPClientConfig config) { - super(REGEX); - configure(config); - } - - /** - * Parses a line of a VMS FTP server file listing and converts it into a - * usable format in the form of an FTPFile instance. If the - * file listing line doesn't describe a file, null is - * returned, otherwise a FTPFile instance representing the - * files in the directory is returned. - * - * @param entry A line of text from the file listing - * @return An FTPFile instance corresponding to the supplied entry - */ - @Override public FTPFile parseFTPEntry(String entry) { - //one block in VMS equals 512 bytes - long longBlock = 512; - - if (matches(entry)) { - FTPFile f = new FTPFile(); - f.setRawListing(entry); - String name = group(1); - String size = group(2); - String datestr = group(3) + " " + group(4); - String owner = group(5); - String permissions[] = new String[3]; - permissions[0] = group(9); - permissions[1] = group(10); - permissions[2] = group(11); - try { - f.setTimestamp(super.parseTimestamp(datestr)); - } catch (ParseException e) { - // intentionally do nothing - } - - String grp; - String user; - StringTokenizer t = new StringTokenizer(owner, ","); - switch (t.countTokens()) { - case 1: - grp = null; - user = t.nextToken(); - break; - case 2: - grp = t.nextToken(); - user = t.nextToken(); - break; - default: - grp = null; - user = null; - } - - if (name.lastIndexOf(".DIR") != -1) { - f.setType(FTPFile.DIRECTORY_TYPE); - } else { - f.setType(FTPFile.FILE_TYPE); - } - //set FTPFile name - //Check also for versions to be returned or not - if (isVersioning()) { - f.setName(name); - } else { - name = name.substring(0, name.lastIndexOf(";")); - f.setName(name); - } - //size is retreived in blocks and needs to be put in bytes - //for us humans and added to the FTPFile array - long sizeInBytes = Long.parseLong(size) * longBlock; - f.setSize(sizeInBytes); - - f.setGroup(grp); - f.setUser(user); - //set group and owner - - //Set file permission. - //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain - //R (read) W (write) E (execute) D (delete) - - //iterate for OWNER GROUP WORLD permissions - for (int access = 0; access < 3; access++) { - String permission = permissions[access]; - - f.setPermission(access, FTPFile.READ_PERMISSION, permission.indexOf('R') >= 0); - f.setPermission(access, FTPFile.WRITE_PERMISSION, permission.indexOf('W') >= 0); - f.setPermission(access, FTPFile.EXECUTE_PERMISSION, permission.indexOf('E') >= 0); - } - - return f; - } - return null; - } - - /** - * Reads the next entry using the supplied BufferedReader object up to - * whatever delemits one entry from the next. This parser cannot use - * the default implementation of simply calling BufferedReader.readLine(), - * because one entry may span multiple lines. - * - * @param reader The BufferedReader object from which entries are to be - * read. - * @return A string representing the next ftp entry or null if none found. - * @throws IOException thrown on any IO Error reading from the reader. - */ - @Override public String readNextEntry(BufferedReader reader) throws IOException { - String line = reader.readLine(); - StringBuilder entry = new StringBuilder(); - while (line != null) { - if (line.startsWith("Directory") || line.startsWith("Total")) { - line = reader.readLine(); - continue; - } - - entry.append(line); - if (line.trim().endsWith(")")) { - break; - } - line = reader.readLine(); - } - return (entry.length() == 0 ? null : entry.toString()); - } - - protected boolean isVersioning() { - return false; - } - - /** - * Defines a default configuration to be used when this class is - * instantiated without a {@link FTPClientConfig FTPClientConfig} - * parameter being specified. - * - * @return the default configuration for this parser. - */ - @Override protected FTPClientConfig getDefaultConfiguration() { - return new FTPClientConfig(FTPClientConfig.SYST_VMS, DEFAULT_DATE_FORMAT, null); - } - - // DEPRECATED METHODS - for API compatibility only - DO NOT USE - - /** - * DO NOT USE - * - * @param listStream the stream - * @return the array of files - * @throws IOException on error - * @deprecated (2.2) No other FTPFileEntryParser implementations have this method. - */ - @Deprecated public FTPFile[] parseFileList(java.io.InputStream listStream) throws IOException { - FTPListParseEngine engine = - new FTPListParseEngine(this); - engine.readServerList(listStream, null); - return engine.getFiles(); - } -} - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java deleted file mode 100644 index 948662a7..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/VMSVersioningFTPEntryParser.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.ftp.parser; - -import aria.apache.commons.net.ftp.FTPClientConfig; -import aria.apache.commons.net.ftp.FTPFileEntryParser; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * Special implementation VMSFTPEntryParser with versioning turned on. - * This parser removes all duplicates and only leaves the version with the highest - * version number for each filename. - * - * This is a sample of VMS LIST output - * - * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] - * (RWED,RWED,RWED,RE)", - *

- * - * @version $Id: VMSVersioningFTPEntryParser.java 1747119 2016-06-07 02:22:24Z ggregory $ - * @see FTPFileEntryParser FTPFileEntryParser (for usage instructions) - */ -public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser { - - private final Pattern _preparse_pattern_; - private static final String PRE_PARSE_REGEX = "(.*?);([0-9]+)\\s*.*"; - - /** - * Constructor for a VMSFTPEntryParser object. - * - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - */ - public VMSVersioningFTPEntryParser() { - this(null); - } - - /** - * This constructor allows the creation of a VMSVersioningFTPEntryParser - * object with something other than the default configuration. - * - * @param config The {@link FTPClientConfig configuration} object used to - * configure this parser. - * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not - * be seen - * under normal conditions. It it is seen, this is a sign that - * REGEX is not a valid regular expression. - * @since 1.4 - */ - public VMSVersioningFTPEntryParser(FTPClientConfig config) { - super(); - configure(config); - try { - //_preparse_matcher_ = new Perl5Matcher(); - _preparse_pattern_ = Pattern.compile(PRE_PARSE_REGEX); - } catch (PatternSyntaxException pse) { - throw new IllegalArgumentException("Unparseable regex supplied: " + PRE_PARSE_REGEX); - } - } - - /** - * Implement hook provided for those implementers (such as - * VMSVersioningFTPEntryParser, and possibly others) which return - * multiple files with the same name to remove the duplicates .. - * - * @param original Original list - * @return Original list purged of duplicates - */ - @Override public List preParse(List original) { - HashMap existingEntries = new HashMap(); - ListIterator iter = original.listIterator(); - while (iter.hasNext()) { - String entry = iter.next().trim(); - MatchResult result = null; - Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); - if (_preparse_matcher_.matches()) { - result = _preparse_matcher_.toMatchResult(); - String name = result.group(1); - String version = result.group(2); - Integer nv = Integer.valueOf(version); - Integer existing = existingEntries.get(name); - if (null != existing) { - if (nv.intValue() < existing.intValue()) { - iter.remove(); // removes older version from original list. - continue; - } - } - existingEntries.put(name, nv); - } - } - // we've now removed all entries less than with less than the largest - // version number for each name that were listed after the largest. - // we now must remove those with smaller than the largest version number - // for each name that were found before the largest - while (iter.hasPrevious()) { - String entry = iter.previous().trim(); - MatchResult result = null; - Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry); - if (_preparse_matcher_.matches()) { - result = _preparse_matcher_.toMatchResult(); - String name = result.group(1); - String version = result.group(2); - Integer nv = Integer.valueOf(version); - Integer existing = existingEntries.get(name); - if (null != existing) { - if (nv.intValue() < existing.intValue()) { - iter.remove(); // removes older version from original list. - } - } - } - } - return original; - } - - @Override protected boolean isVersioning() { - return true; - } -} - -/* Emacs configuration - * Local variables: ** - * mode: java ** - * c-basic-offset: 4 ** - * indent-tabs-mode: nil ** - * End: ** - */ diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java deleted file mode 100644 index 147da7ad..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/ftp/parser/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * FTP file listing parser classes - */ -package aria.apache.commons.net.ftp.parser; \ No newline at end of file diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java deleted file mode 100644 index 593214d7..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CRLFLineReader.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -/** - * CRLFLineReader implements a readLine() method that requires - * exactly CRLF to terminate an input line. - * This is required for IMAP, which allows bare CR and LF. - * - * @since 3.0 - */ -public final class CRLFLineReader extends BufferedReader { - private static final char LF = '\n'; - private static final char CR = '\r'; - - /** - * Creates a CRLFLineReader that wraps an existing Reader - * input source. - * - * @param reader The Reader input source. - */ - public CRLFLineReader(Reader reader) { - super(reader); - } - - /** - * Read a line of text. - * A line is considered to be terminated by carriage return followed immediately by a linefeed. - * This contrasts with BufferedReader which also allows other combinations. - * - * @since 3.0 - */ - @Override public String readLine() throws IOException { - StringBuilder sb = new StringBuilder(); - int intch; - boolean prevWasCR = false; - synchronized (lock) { // make thread-safe (hopefully!) - while ((intch = read()) != -1) { - if (prevWasCR && intch == LF) { - return sb.substring(0, sb.length() - 1); - } - if (intch == CR) { - prevWasCR = true; - } else { - prevWasCR = false; - } - sb.append((char) intch); - } - } - String string = sb.toString(); - if (string.length() == 0) { // immediate EOF - return null; - } - return string; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java deleted file mode 100644 index 0f67dda9..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamAdapter.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.util.EventListener; - -import aria.apache.commons.net.util.ListenerList; - -/** - * The CopyStreamAdapter will relay CopyStreamEvents to a list of listeners - * when either of its bytesTransferred() methods are called. Its purpose - * is to facilitate the notification of the progress of a copy operation - * performed by one of the static copyStream() methods in - * org.apache.commons.io.Util to multiple listeners. The static - * copyStream() methods invoke the - * bytesTransfered(long, int) of a CopyStreamListener for performance - * reasons and also because multiple listeners cannot be registered given - * that the methods are static. - * - * @version $Id: CopyStreamAdapter.java 1741829 2016-05-01 00:24:44Z sebb $ - * @see CopyStreamEvent - * @see CopyStreamListener - * @see Util - */ -public class CopyStreamAdapter implements CopyStreamListener { - private final ListenerList internalListeners; - - /** - * Creates a new copyStreamAdapter. - */ - public CopyStreamAdapter() { - internalListeners = new ListenerList(); - } - - /** - * This method is invoked by a CopyStreamEvent source after copying - * a block of bytes from a stream. The CopyStreamEvent will contain - * the total number of bytes transferred so far and the number of bytes - * transferred in the last write. The CopyStreamAdapater will relay - * the event to all of its registered listeners, listing itself as the - * source of the event. - * - * @param event The CopyStreamEvent fired by the copying of a block of - * bytes. - */ - @Override public void bytesTransferred(CopyStreamEvent event) { - for (EventListener listener : internalListeners) { - ((CopyStreamListener) (listener)).bytesTransferred(event); - } - } - - /** - * This method is not part of the JavaBeans model and is used by the - * static methods in the org.apache.commons.io.Util class for efficiency. - * It is invoked after a block of bytes to inform the listener of the - * transfer. The CopyStreamAdapater will create a CopyStreamEvent - * from the arguments and relay the event to all of its registered - * listeners, listing itself as the source of the event. - * - * @param totalBytesTransferred The total number of bytes transferred - * so far by the copy operation. - * @param bytesTransferred The number of bytes copied by the most recent - * write. - * @param streamSize The number of bytes in the stream being copied. - * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if - * the size is unknown. - */ - @Override public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, - long streamSize) { - for (EventListener listener : internalListeners) { - ((CopyStreamListener) (listener)).bytesTransferred(totalBytesTransferred, bytesTransferred, - streamSize); - } - } - - /** - * Registers a CopyStreamListener to receive CopyStreamEvents. - * Although this method is not declared to be synchronized, it is - * implemented in a thread safe manner. - * - * @param listener The CopyStreamlistener to register. - */ - public void addCopyStreamListener(CopyStreamListener listener) { - internalListeners.addListener(listener); - } - - /** - * Unregisters a CopyStreamListener. Although this method is not - * synchronized, it is implemented in a thread safe manner. - * - * @param listener The CopyStreamlistener to unregister. - */ - public void removeCopyStreamListener(CopyStreamListener listener) { - internalListeners.removeListener(listener); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java deleted file mode 100644 index f44d918e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamEvent.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.util.EventObject; - -/** - * A CopyStreamEvent is triggered after every write performed by a - * stream copying operation. The event stores the number of bytes - * transferred by the write triggering the event as well as the total - * number of bytes transferred so far by the copy operation. - * - * @version $Id: CopyStreamEvent.java 1652801 2015-01-18 17:10:05Z sebb $ - * @see CopyStreamListener - * @see CopyStreamAdapter - * @see Util - */ -public class CopyStreamEvent extends EventObject { - private static final long serialVersionUID = -964927635655051867L; - - /** - * Constant used to indicate the stream size is unknown. - */ - public static final long UNKNOWN_STREAM_SIZE = -1; - - private final int bytesTransferred; - private final long totalBytesTransferred; - private final long streamSize; - - /** - * Creates a new CopyStreamEvent instance. - * - * @param source The source of the event. - * @param totalBytesTransferred The total number of bytes transferred so - * far during a copy operation. - * @param bytesTransferred The number of bytes transferred during the - * write that triggered the CopyStreamEvent. - * @param streamSize The number of bytes in the stream being copied. - * This may be set to UNKNOWN_STREAM_SIZE if the - * size is unknown. - */ - public CopyStreamEvent(Object source, long totalBytesTransferred, int bytesTransferred, - long streamSize) { - super(source); - this.bytesTransferred = bytesTransferred; - this.totalBytesTransferred = totalBytesTransferred; - this.streamSize = streamSize; - } - - /** - * Returns the number of bytes transferred by the write that triggered - * the event. - * - * @return The number of bytes transferred by the write that triggered - * the vent. - */ - public int getBytesTransferred() { - return bytesTransferred; - } - - /** - * Returns the total number of bytes transferred so far by the copy - * operation. - * - * @return The total number of bytes transferred so far by the copy - * operation. - */ - public long getTotalBytesTransferred() { - return totalBytesTransferred; - } - - /** - * Returns the size of the stream being copied. - * This may be set to UNKNOWN_STREAM_SIZE if the - * size is unknown. - * - * @return The size of the stream being copied. - */ - public long getStreamSize() { - return streamSize; - } - - /** - * @since 3.0 - */ - @Override public String toString() { - return getClass().getName() - + "[source=" - + source - + ", total=" - + totalBytesTransferred - + ", bytes=" - + bytesTransferred - + ", size=" - + streamSize - + "]"; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamException.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamException.java deleted file mode 100644 index 4314967c..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamException.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.IOException; - -/** - * The CopyStreamException class is thrown by the org.apache.commons.io.Util - * copyStream() methods. It stores the number of bytes confirmed to - * have been transferred before an I/O error as well as the IOException - * responsible for the failure of a copy operation. - * - * @version $Id: CopyStreamException.java 1299238 2012-03-10 17:12:28Z sebb $ - * @see Util - */ -public class CopyStreamException extends IOException { - private static final long serialVersionUID = -2602899129433221532L; - - private final long totalBytesTransferred; - - /** - * Creates a new CopyStreamException instance. - * - * @param message A message describing the error. - * @param bytesTransferred The total number of bytes transferred before - * an exception was thrown in a copy operation. - * @param exception The IOException thrown during a copy operation. - */ - public CopyStreamException(String message, long bytesTransferred, IOException exception) { - super(message); - initCause(exception); // merge this into super() call once we need 1.6+ - totalBytesTransferred = bytesTransferred; - } - - /** - * Returns the total number of bytes confirmed to have - * been transferred by a failed copy operation. - * - * @return The total number of bytes confirmed to have - * been transferred by a failed copy operation. - */ - public long getTotalBytesTransferred() { - return totalBytesTransferred; - } - - /** - * Returns the IOException responsible for the failure of a copy operation. - * - * @return The IOException responsible for the failure of a copy operation. - */ - public IOException getIOException() { - return (IOException) getCause(); // cast is OK because it was initialised with an IOException - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java deleted file mode 100644 index 14670feb..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/CopyStreamListener.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.util.EventListener; - -/** - * The CopyStreamListener class can accept CopyStreamEvents to keep track - * of the progress of a stream copying operation. However, it is currently - * not used that way within NetComponents for performance reasons. Rather - * the bytesTransferred(long, int) method is called directly rather than - * passing an event to bytesTransferred(CopyStreamEvent), saving the creation - * of a CopyStreamEvent instance. Also, the only place where - * CopyStreamListener is currently used within NetComponents is in the - * static methods of the uninstantiable org.apache.commons.io.Util class, which - * would preclude the use of addCopyStreamListener and - * removeCopyStreamListener methods. However, future additions may use the - * JavaBean event model, which is why the hooks have been included from the - * beginning. - * - * @version $Id: CopyStreamListener.java 1652801 2015-01-18 17:10:05Z sebb $ - * @see CopyStreamEvent - * @see CopyStreamAdapter - * @see Util - */ -public interface CopyStreamListener extends EventListener { - /** - * This method is invoked by a CopyStreamEvent source after copying - * a block of bytes from a stream. The CopyStreamEvent will contain - * the total number of bytes transferred so far and the number of bytes - * transferred in the last write. - * - * @param event The CopyStreamEvent fired by the copying of a block of - * bytes. - */ - public void bytesTransferred(CopyStreamEvent event); - - /** - * This method is not part of the JavaBeans model and is used by the - * static methods in the org.apache.commons.io.Util class for efficiency. - * It is invoked after a block of bytes to inform the listener of the - * transfer. - * - * @param totalBytesTransferred The total number of bytes transferred - * so far by the copy operation. - * @param bytesTransferred The number of bytes copied by the most recent - * write. - * @param streamSize The number of bytes in the stream being copied. - * This may be equal to CopyStreamEvent.UNKNOWN_STREAM_SIZE if - * the size is unknown. - */ - public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize); -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java deleted file mode 100644 index e554a359..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageReader.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -/** - * DotTerminatedMessageReader is a class used to read messages from a - * server that are terminated by a single dot followed by a - * <CR><LF> - * sequence and with double dots appearing at the begining of lines which - * do not signal end of message yet start with a dot. Various Internet - * protocols such as NNTP and POP3 produce messages of this type. - *

- * This class handles stripping of the duplicate period at the beginning - * of lines starting with a period, and ensures you cannot read past the end of the message. - *

- * Note: versions since 3.0 extend BufferedReader rather than Reader, - * and no longer change the CRLF into the local EOL. Also only DOT CR LF - * acts as EOF. - * - * @version $Id: DotTerminatedMessageReader.java 1747119 2016-06-07 02:22:24Z ggregory $ - */ -public final class DotTerminatedMessageReader extends BufferedReader { - private static final char LF = '\n'; - private static final char CR = '\r'; - private static final int DOT = '.'; - - private boolean atBeginning; - private boolean eof; - private boolean seenCR; // was last character CR? - - /** - * Creates a DotTerminatedMessageReader that wraps an existing Reader - * input source. - * - * @param reader The Reader input source containing the message. - */ - public DotTerminatedMessageReader(Reader reader) { - super(reader); - // Assumes input is at start of message - atBeginning = true; - eof = false; - } - - /** - * Reads and returns the next character in the message. If the end of the - * message has been reached, returns -1. Note that a call to this method - * may result in multiple reads from the underlying input stream to decode - * the message properly (removing doubled dots and so on). All of - * this is transparent to the programmer and is only mentioned for - * completeness. - * - * @return The next character in the message. Returns -1 if the end of the - * message has been reached. - * @throws IOException If an error occurs while reading the underlying - * stream. - */ - @Override public int read() throws IOException { - synchronized (lock) { - if (eof) { - return -1; // Don't allow read past EOF - } - int chint = super.read(); - if (chint == -1) { // True EOF - eof = true; - return -1; - } - if (atBeginning) { - atBeginning = false; - if (chint == DOT) { // Have DOT - mark(2); // need to check for CR LF or DOT - chint = super.read(); - if (chint == -1) { // Should not happen - // new Throwable("Trailing DOT").printStackTrace(); - eof = true; - return DOT; // return the trailing DOT - } - if (chint == DOT) { // Have DOT DOT - // no need to reset as we want to lose the first DOT - return chint; // i.e. DOT - } - if (chint == CR) { // Have DOT CR - chint = super.read(); - if (chint == -1) { // Still only DOT CR - should not happen - //new Throwable("Trailing DOT CR").printStackTrace(); - reset(); // So CR is picked up next time - return DOT; // return the trailing DOT - } - if (chint == LF) { // DOT CR LF - atBeginning = true; - eof = true; - // Do we need to clear the mark somehow? - return -1; - } - } - // Should not happen - lone DOT at beginning - //new Throwable("Lone DOT followed by "+(char)chint).printStackTrace(); - reset(); - return DOT; - } // have DOT - } // atBeginning - - // Handle CRLF in normal flow - if (seenCR) { - seenCR = false; - if (chint == LF) { - atBeginning = true; - } - } - if (chint == CR) { - seenCR = true; - } - return chint; - } - } - - /** - * Reads the next characters from the message into an array and - * returns the number of characters read. Returns -1 if the end of the - * message has been reached. - * - * @param buffer The character array in which to store the characters. - * @return The number of characters read. Returns -1 if the - * end of the message has been reached. - * @throws IOException If an error occurs in reading the underlying - * stream. - */ - @Override public int read(char[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - /** - * Reads the next characters from the message into an array and - * returns the number of characters read. Returns -1 if the end of the - * message has been reached. The characters are stored in the array - * starting from the given offset and up to the length specified. - * - * @param buffer The character array in which to store the characters. - * @param offset The offset into the array at which to start storing - * characters. - * @param length The number of characters to read. - * @return The number of characters read. Returns -1 if the - * end of the message has been reached. - * @throws IOException If an error occurs in reading the underlying - * stream. - */ - @Override public int read(char[] buffer, int offset, int length) throws IOException { - if (length < 1) { - return 0; - } - int ch; - synchronized (lock) { - if ((ch = read()) == -1) { - return -1; - } - - int off = offset; - - do { - buffer[offset++] = (char) ch; - } while (--length > 0 && (ch = read()) != -1); - - return (offset - off); - } - } - - /** - * Closes the message for reading. This doesn't actually close the - * underlying stream. The underlying stream may still be used for - * communicating with the server and therefore is not closed. - *

- * If the end of the message has not yet been reached, this method - * will read the remainder of the message until it reaches the end, - * so that the underlying stream may continue to be used properly - * for communicating with the server. If you do not fully read - * a message, you MUST close it, otherwise your program will likely - * hang or behave improperly. - * - * @throws IOException If an error occurs while reading the - * underlying stream. - */ - @Override public void close() throws IOException { - synchronized (lock) { - if (!eof) { - while (read() != -1) { - // read to EOF - } - } - eof = true; - atBeginning = false; - } - } - - /** - * Read a line of text. - * A line is considered to be terminated by carriage return followed immediately by a linefeed. - * This contrasts with BufferedReader which also allows other combinations. - * - * @since 3.0 - */ - @Override public String readLine() throws IOException { - StringBuilder sb = new StringBuilder(); - int intch; - synchronized (lock) { // make thread-safe (hopefully!) - while ((intch = read()) != -1) { - if (intch == LF && atBeginning) { - return sb.substring(0, sb.length() - 1); - } - sb.append((char) intch); - } - } - String string = sb.toString(); - if (string.length() == 0) { // immediate EOF - return null; - } - // Should not happen - EOF without CRLF - //new Throwable(string).printStackTrace(); - return string; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java deleted file mode 100644 index 637399bd..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/DotTerminatedMessageWriter.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.IOException; -import java.io.Writer; - -/*** - * DotTerminatedMessageWriter is a class used to write messages to a - * server that are terminated by a single dot followed by a - * <CR><LF> - * sequence and with double dots appearing at the begining of lines which - * do not signal end of message yet start with a dot. Various Internet - * protocols such as NNTP and POP3 produce messages of this type. - *

- * This class handles the doubling of line-starting periods, - * converts single linefeeds to NETASCII newlines, and on closing - * will send the final message terminator dot and NETASCII newline - * sequence. - * - * - ***/ - -public final class DotTerminatedMessageWriter extends Writer { - private static final int __NOTHING_SPECIAL_STATE = 0; - private static final int __LAST_WAS_CR_STATE = 1; - private static final int __LAST_WAS_NL_STATE = 2; - - private int __state; - private Writer __output; - - /*** - * Creates a DotTerminatedMessageWriter that wraps an existing Writer - * output destination. - * - * @param output The Writer output destination to write the message. - ***/ - public DotTerminatedMessageWriter(Writer output) { - super(output); - __output = output; - __state = __NOTHING_SPECIAL_STATE; - } - - /*** - * Writes a character to the output. Note that a call to this method - * may result in multiple writes to the underling Writer in order to - * convert naked linefeeds to NETASCII line separators and to double - * line-leading periods. This is transparent to the programmer and - * is only mentioned for completeness. - * - * @param ch The character to write. - * @throws IOException If an error occurs while writing to the - * underlying output. - ***/ - @Override public void write(int ch) throws IOException { - synchronized (lock) { - switch (ch) { - case '\r': - __state = __LAST_WAS_CR_STATE; - __output.write('\r'); - return; - case '\n': - if (__state != __LAST_WAS_CR_STATE) { - __output.write('\r'); - } - __output.write('\n'); - __state = __LAST_WAS_NL_STATE; - return; - case '.': - // Double the dot at the beginning of a line - if (__state == __LAST_WAS_NL_STATE) { - __output.write('.'); - } - //$FALL-THROUGH$ - default: - __state = __NOTHING_SPECIAL_STATE; - __output.write(ch); - return; - } - } - } - - /*** - * Writes a number of characters from a character array to the output - * starting from a given offset. - * - * @param buffer The character array to write. - * @param offset The offset into the array at which to start copying data. - * @param length The number of characters to write. - * @throws IOException If an error occurs while writing to the underlying - * output. - ***/ - @Override public void write(char[] buffer, int offset, int length) throws IOException { - synchronized (lock) { - while (length-- > 0) { - write(buffer[offset++]); - } - } - } - - /*** - * Writes a character array to the output. - * - * @param buffer The character array to write. - * @throws IOException If an error occurs while writing to the underlying - * output. - ***/ - @Override public void write(char[] buffer) throws IOException { - write(buffer, 0, buffer.length); - } - - /*** - * Writes a String to the output. - * - * @param string The String to write. - * @throws IOException If an error occurs while writing to the underlying - * output. - ***/ - @Override public void write(String string) throws IOException { - write(string.toCharArray()); - } - - /*** - * Writes part of a String to the output starting from a given offset. - * - * @param string The String to write. - * @param offset The offset into the String at which to start copying data. - * @param length The number of characters to write. - * @throws IOException If an error occurs while writing to the underlying - * output. - ***/ - @Override public void write(String string, int offset, int length) throws IOException { - write(string.toCharArray(), offset, length); - } - - /*** - * Flushes the underlying output, writing all buffered output. - * - * @throws IOException If an error occurs while writing to the underlying - * output. - ***/ - @Override public void flush() throws IOException { - synchronized (lock) { - __output.flush(); - } - } - - /*** - * Flushes the underlying output, writing all buffered output, but doesn't - * actually close the underlying stream. The underlying stream may still - * be used for communicating with the server and therefore is not closed. - * - * @throws IOException If an error occurs while writing to the underlying - * output or closing the Writer. - ***/ - @Override public void close() throws IOException { - synchronized (lock) { - if (__output == null) { - return; - } - - if (__state == __LAST_WAS_CR_STATE) { - __output.write('\n'); - } else if (__state != __LAST_WAS_NL_STATE) { - __output.write("\r\n"); - } - - __output.write(".\r\n"); - - __output.flush(); - __output = null; - } - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java deleted file mode 100644 index f8aeeded..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIInputStream.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PushbackInputStream; -import java.io.UnsupportedEncodingException; - -/*** - * This class wraps an input stream, replacing all occurrences - * of <CR><LF> (carriage return followed by a linefeed), - * which is the NETASCII standard for representing a newline, with the - * local line separator representation. You would use this class to - * implement ASCII file transfers requiring conversion from NETASCII. - * - * - ***/ - -public final class FromNetASCIIInputStream extends PushbackInputStream { - static final boolean _noConversionRequired; - static final String _lineSeparator; - static final byte[] _lineSeparatorBytes; - - static { - _lineSeparator = System.getProperty("line.separator"); - _noConversionRequired = _lineSeparator.equals("\r\n"); - try { - _lineSeparatorBytes = _lineSeparator.getBytes("US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Broken JVM - cannot find US-ASCII charset!", e); - } - } - - private int __length = 0; - - /*** - * Returns true if the NetASCII line separator differs from the system - * line separator, false if they are the same. This method is useful - * to determine whether or not you need to instantiate a - * FromNetASCIIInputStream object. - * - * @return True if the NETASCII line separator differs from the local - * system line separator, false if they are the same. - ***/ - public static final boolean isConversionRequired() { - return !_noConversionRequired; - } - - /*** - * Creates a FromNetASCIIInputStream instance that wraps an existing - * InputStream. - * @param input the stream to wrap - ***/ - public FromNetASCIIInputStream(InputStream input) { - super(input, _lineSeparatorBytes.length + 1); - } - - private int __read() throws IOException { - int ch; - - ch = super.read(); - - if (ch == '\r') { - ch = super.read(); - if (ch == '\n') { - unread(_lineSeparatorBytes); - ch = super.read(); - // This is a kluge for read(byte[], ...) to read the right amount - --__length; - } else { - if (ch != -1) { - unread(ch); - } - return '\r'; - } - } - - return ch; - } - - /*** - * Reads and returns the next byte in the stream. If the end of the - * message has been reached, returns -1. Note that a call to this method - * may result in multiple reads from the underlying input stream in order - * to convert NETASCII line separators to the local line separator format. - * This is transparent to the programmer and is only mentioned for - * completeness. - * - * @return The next character in the stream. Returns -1 if the end of the - * stream has been reached. - * @throws IOException If an error occurs while reading the underlying - * stream. - ***/ - @Override public int read() throws IOException { - if (_noConversionRequired) { - return super.read(); - } - - return __read(); - } - - /*** - * Reads the next number of bytes from the stream into an array and - * returns the number of bytes read. Returns -1 if the end of the - * stream has been reached. - * - * @param buffer The byte array in which to store the data. - * @return The number of bytes read. Returns -1 if the - * end of the message has been reached. - * @throws IOException If an error occurs in reading the underlying - * stream. - ***/ - @Override public int read(byte buffer[]) throws IOException { - return read(buffer, 0, buffer.length); - } - - /*** - * Reads the next number of bytes from the stream into an array and returns - * the number of bytes read. Returns -1 if the end of the - * message has been reached. The characters are stored in the array - * starting from the given offset and up to the length specified. - * - * @param buffer The byte array in which to store the data. - * @param offset The offset into the array at which to start storing data. - * @param length The number of bytes to read. - * @return The number of bytes read. Returns -1 if the - * end of the stream has been reached. - * @throws IOException If an error occurs while reading the underlying - * stream. - ***/ - @Override public int read(byte buffer[], int offset, int length) throws IOException { - if (_noConversionRequired) { - return super.read(buffer, offset, length); - } - - if (length < 1) { - return 0; - } - - int ch, off; - - ch = available(); - - __length = (length > ch ? ch : length); - - // If nothing is available, block to read only one character - if (__length < 1) { - __length = 1; - } - - if ((ch = __read()) == -1) { - return -1; - } - - off = offset; - - do { - buffer[offset++] = (byte) ch; - } while (--__length > 0 && (ch = __read()) != -1); - - return (offset - off); - } - - // PushbackInputStream in JDK 1.1.3 returns the wrong thing - // TODO - can we delete this override now? - - /*** - * Returns the number of bytes that can be read without blocking EXCEPT - * when newline conversions have to be made somewhere within the - * available block of bytes. In other words, you really should not - * rely on the value returned by this method if you are trying to avoid - * blocking. - ***/ - @Override public int available() throws IOException { - if (in == null) { - throw new IOException("Stream closed"); - } - return (buf.length - pos) + in.available(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java deleted file mode 100644 index 85e6112d..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/FromNetASCIIOutputStream.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/*** - * This class wraps an output stream, replacing all occurrences - * of <CR><LF> (carriage return followed by a linefeed), - * which is the NETASCII standard for representing a newline, with the - * local line separator representation. You would use this class to - * implement ASCII file transfers requiring conversion from NETASCII. - *

- * Because of the translation process, a call to flush() will - * not flush the last byte written if that byte was a carriage - * return. A call to {@link #close close() }, however, will - * flush the carriage return. - * - * - ***/ - -public final class FromNetASCIIOutputStream extends FilterOutputStream { - private boolean __lastWasCR; - - /*** - * Creates a FromNetASCIIOutputStream instance that wraps an existing - * OutputStream. - * - * @param output The OutputStream to wrap. - ***/ - public FromNetASCIIOutputStream(OutputStream output) { - super(output); - __lastWasCR = false; - } - - private void __write(int ch) throws IOException { - switch (ch) { - case '\r': - __lastWasCR = true; - // Don't write anything. We need to see if next one is linefeed - break; - case '\n': - if (__lastWasCR) { - out.write(FromNetASCIIInputStream._lineSeparatorBytes); - __lastWasCR = false; - break; - } - __lastWasCR = false; - out.write('\n'); - break; - default: - if (__lastWasCR) { - out.write('\r'); - __lastWasCR = false; - } - out.write(ch); - break; - } - } - - /*** - * Writes a byte to the stream. Note that a call to this method - * might not actually write a byte to the underlying stream until a - * subsequent character is written, from which it can be determined if - * a NETASCII line separator was encountered. - * This is transparent to the programmer and is only mentioned for - * completeness. - * - * @param ch The byte to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(int ch) throws IOException { - if (FromNetASCIIInputStream._noConversionRequired) { - out.write(ch); - return; - } - - __write(ch); - } - - /*** - * Writes a byte array to the stream. - * - * @param buffer The byte array to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(byte buffer[]) throws IOException { - write(buffer, 0, buffer.length); - } - - /*** - * Writes a number of bytes from a byte array to the stream starting from - * a given offset. - * - * @param buffer The byte array to write. - * @param offset The offset into the array at which to start copying data. - * @param length The number of bytes to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(byte buffer[], int offset, int length) - throws IOException { - if (FromNetASCIIInputStream._noConversionRequired) { - // FilterOutputStream method is very slow. - //super.write(buffer, offset, length); - out.write(buffer, offset, length); - return; - } - - while (length-- > 0) { - __write(buffer[offset++]); - } - } - - /*** - * Closes the stream, writing all pending data. - * - * @throws IOException If an error occurs while closing the stream. - ***/ - @Override public synchronized void close() throws IOException { - if (FromNetASCIIInputStream._noConversionRequired) { - super.close(); - return; - } - - if (__lastWasCR) { - out.write('\r'); - } - super.close(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketInputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketInputStream.java deleted file mode 100644 index 531e4029..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketInputStream.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.Socket; - -/*** - * This class wraps an input stream, storing a reference to its originating - * socket. When the stream is closed, it will also close the socket - * immediately afterward. This class is useful for situations where you - * are dealing with a stream originating from a socket, but do not have - * a reference to the socket, and want to make sure it closes when the - * stream closes. - * - * - * @see SocketOutputStream - ***/ - -public class SocketInputStream extends FilterInputStream { - private final Socket __socket; - - /*** - * Creates a SocketInputStream instance wrapping an input stream and - * storing a reference to a socket that should be closed on closing - * the stream. - * - * @param socket The socket to close on closing the stream. - * @param stream The input stream to wrap. - ***/ - public SocketInputStream(Socket socket, InputStream stream) { - super(stream); - __socket = socket; - } - - /*** - * Closes the stream and immediately afterward closes the referenced - * socket. - * - * @throws IOException If there is an error in closing the stream - * or socket. - ***/ - @Override public void close() throws IOException { - super.close(); - __socket.close(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java deleted file mode 100644 index 3393ded7..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/SocketOutputStream.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.Socket; - -/*** - * This class wraps an output stream, storing a reference to its originating - * socket. When the stream is closed, it will also close the socket - * immediately afterward. This class is useful for situations where you - * are dealing with a stream originating from a socket, but do not have - * a reference to the socket, and want to make sure it closes when the - * stream closes. - * - * - * @see SocketInputStream - ***/ - -public class SocketOutputStream extends FilterOutputStream { - private final Socket __socket; - - /*** - * Creates a SocketOutputStream instance wrapping an output stream and - * storing a reference to a socket that should be closed on closing - * the stream. - * - * @param socket The socket to close on closing the stream. - * @param stream The input stream to wrap. - ***/ - public SocketOutputStream(Socket socket, OutputStream stream) { - super(stream); - __socket = socket; - } - - /*** - * Writes a number of bytes from a byte array to the stream starting from - * a given offset. This method bypasses the equivalent method in - * FilterOutputStream because the FilterOutputStream implementation is - * very inefficient. - * - * @param buffer The byte array to write. - * @param offset The offset into the array at which to start copying data. - * @param length The number of bytes to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public void write(byte buffer[], int offset, int length) throws IOException { - out.write(buffer, offset, length); - } - - /*** - * Closes the stream and immediately afterward closes the referenced - * socket. - * - * @throws IOException If there is an error in closing the stream - * or socket. - ***/ - @Override public void close() throws IOException { - super.close(); - __socket.close(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java deleted file mode 100644 index e7ee8bc5..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIInputStream.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -/*** - * This class wraps an input stream, replacing all singly occurring - * <LF> (linefeed) characters with <CR><LF> (carriage return - * followed by linefeed), which is the NETASCII standard for representing - * a newline. - * You would use this class to implement ASCII file transfers requiring - * conversion to NETASCII. - * - * - ***/ - -public final class ToNetASCIIInputStream extends FilterInputStream { - private static final int __NOTHING_SPECIAL = 0; - private static final int __LAST_WAS_CR = 1; - private static final int __LAST_WAS_NL = 2; - private int __status; - - /*** - * Creates a ToNetASCIIInputStream instance that wraps an existing - * InputStream. - * - * @param input The InputStream to wrap. - ***/ - public ToNetASCIIInputStream(InputStream input) { - super(input); - __status = __NOTHING_SPECIAL; - } - - /*** - * Reads and returns the next byte in the stream. If the end of the - * message has been reached, returns -1. - * - * @return The next character in the stream. Returns -1 if the end of the - * stream has been reached. - * @throws IOException If an error occurs while reading the underlying - * stream. - ***/ - @Override public int read() throws IOException { - int ch; - - if (__status == __LAST_WAS_NL) { - __status = __NOTHING_SPECIAL; - return '\n'; - } - - ch = in.read(); - - switch (ch) { - case '\r': - __status = __LAST_WAS_CR; - return '\r'; - case '\n': - if (__status != __LAST_WAS_CR) { - __status = __LAST_WAS_NL; - return '\r'; - } - //$FALL-THROUGH$ - default: - __status = __NOTHING_SPECIAL; - return ch; - } - // statement not reached - //return ch; - } - - /*** - * Reads the next number of bytes from the stream into an array and - * returns the number of bytes read. Returns -1 if the end of the - * stream has been reached. - * - * @param buffer The byte array in which to store the data. - * @return The number of bytes read. Returns -1 if the - * end of the message has been reached. - * @throws IOException If an error occurs in reading the underlying - * stream. - ***/ - @Override public int read(byte buffer[]) throws IOException { - return read(buffer, 0, buffer.length); - } - - /*** - * Reads the next number of bytes from the stream into an array and returns - * the number of bytes read. Returns -1 if the end of the - * message has been reached. The characters are stored in the array - * starting from the given offset and up to the length specified. - * - * @param buffer The byte array in which to store the data. - * @param offset The offset into the array at which to start storing data. - * @param length The number of bytes to read. - * @return The number of bytes read. Returns -1 if the - * end of the stream has been reached. - * @throws IOException If an error occurs while reading the underlying - * stream. - ***/ - @Override public int read(byte buffer[], int offset, int length) throws IOException { - int ch, off; - - if (length < 1) { - return 0; - } - - ch = available(); - - if (length > ch) { - length = ch; - } - - // If nothing is available, block to read only one character - if (length < 1) { - length = 1; - } - - if ((ch = read()) == -1) { - return -1; - } - - off = offset; - - do { - buffer[offset++] = (byte) ch; - } while (--length > 0 && (ch = read()) != -1); - - return (offset - off); - } - - /*** Returns false. Mark is not supported. ***/ - @Override public boolean markSupported() { - return false; - } - - @Override public int available() throws IOException { - int result; - - result = in.available(); - - if (__status == __LAST_WAS_NL) { - return (result + 1); - } - - return result; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java deleted file mode 100644 index d54a8758..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/ToNetASCIIOutputStream.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/*** - * This class wraps an output stream, replacing all singly occurring - * <LF> (linefeed) characters with <CR><LF> (carriage return - * followed by linefeed), which is the NETASCII standard for representing - * a newline. - * You would use this class to implement ASCII file transfers requiring - * conversion to NETASCII. - * - * - ***/ - -public final class ToNetASCIIOutputStream extends FilterOutputStream { - private boolean __lastWasCR; - - /*** - * Creates a ToNetASCIIOutputStream instance that wraps an existing - * OutputStream. - * - * @param output The OutputStream to wrap. - ***/ - public ToNetASCIIOutputStream(OutputStream output) { - super(output); - __lastWasCR = false; - } - - /*** - * Writes a byte to the stream. Note that a call to this method - * may result in multiple writes to the underlying input stream in order - * to convert naked newlines to NETASCII line separators. - * This is transparent to the programmer and is only mentioned for - * completeness. - * - * @param ch The byte to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(int ch) throws IOException { - switch (ch) { - case '\r': - __lastWasCR = true; - out.write('\r'); - return; - case '\n': - if (!__lastWasCR) { - out.write('\r'); - } - //$FALL-THROUGH$ - default: - __lastWasCR = false; - out.write(ch); - return; - } - } - - /*** - * Writes a byte array to the stream. - * - * @param buffer The byte array to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(byte buffer[]) throws IOException { - write(buffer, 0, buffer.length); - } - - /*** - * Writes a number of bytes from a byte array to the stream starting from - * a given offset. - * - * @param buffer The byte array to write. - * @param offset The offset into the array at which to start copying data. - * @param length The number of bytes to write. - * @throws IOException If an error occurs while writing to the underlying - * stream. - ***/ - @Override public synchronized void write(byte buffer[], int offset, int length) - throws IOException { - while (length-- > 0) { - write(buffer[offset++]); - } - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/Util.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/Util.java deleted file mode 100644 index bdf74b76..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/Util.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.io; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.net.Socket; - -/*** - * The Util class cannot be instantiated and stores short static convenience - * methods that are often quite useful. - * - * - * @see CopyStreamException - * @see CopyStreamListener - * @see CopyStreamAdapter - ***/ - -public final class Util { - /** - * The default buffer size ({@value}) used by - * {@link #copyStream copyStream } and {@link #copyReader copyReader} - * and by the copyReader/copyStream methods if a zero or negative buffer size is supplied. - */ - public static final int DEFAULT_COPY_BUFFER_SIZE = 1024; - - // Cannot be instantiated - private Util() { - } - - /*** - * Copies the contents of an InputStream to an OutputStream using a - * copy buffer of a given size and notifies the provided - * CopyStreamListener of the progress of the copy operation by calling - * its bytesTransferred(long, int) method after each write to the - * destination. If you wish to notify more than one listener you should - * use a CopyStreamAdapter as the listener and register the additional - * listeners with the CopyStreamAdapter. - *

- * The contents of the InputStream are - * read until the end of the stream is reached, but neither the - * source nor the destination are closed. You must do this yourself - * outside of the method call. The number of bytes read/written is - * returned. - * - * @param source The source InputStream. - * @param dest The destination OutputStream. - * @param bufferSize The number of bytes to buffer during the copy. - * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. - * @param streamSize The number of bytes in the stream being copied. - * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. - * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} - * @param listener The CopyStreamListener to notify of progress. If - * this parameter is null, notification is not attempted. - * @param flush Whether to flush the output stream after every - * write. This is necessary for interactive sessions that rely on - * buffered streams. If you don't flush, the data will stay in - * the stream buffer. - * @return number of bytes read/written - * @throws CopyStreamException If an error occurs while reading from the - * source or writing to the destination. The CopyStreamException - * will contain the number of bytes confirmed to have been - * transferred before an - * IOException occurred, and it will also contain the IOException - * that caused the error. These values can be retrieved with - * the CopyStreamException getTotalBytesTransferred() and - * getIOException() methods. - ***/ - public static final long copyStream(InputStream source, OutputStream dest, int bufferSize, - long streamSize, CopyStreamListener listener, boolean flush) throws CopyStreamException { - int numBytes; - long total = 0; - byte[] buffer = new byte[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; - - try { - while (!Thread.currentThread().isInterrupted() && (numBytes = source.read(buffer)) != -1) { - // Technically, some read(byte[]) methods may return 0 and we cannot - // accept that as an indication of EOF. - - if (numBytes == 0) { - int singleByte = source.read(); - if (singleByte < 0) { - break; - } - dest.write(singleByte); - if (flush) { - dest.flush(); - } - ++total; - if (listener != null) { - listener.bytesTransferred(total, 1, streamSize); - } - continue; - } - - dest.write(buffer, 0, numBytes); - if (flush) { - dest.flush(); - } - total += numBytes; - if (listener != null) { - listener.bytesTransferred(total, numBytes, streamSize); - } - } - } catch (IOException e) { - throw new CopyStreamException("IOException caught while copying.", total, e); - } - - return total; - } - - - /*** - * Copies the contents of an InputStream to an OutputStream using a - * copy buffer of a given size and notifies the provided - * CopyStreamListener of the progress of the copy operation by calling - * its bytesTransferred(long, int) method after each write to the - * destination. If you wish to notify more than one listener you should - * use a CopyStreamAdapter as the listener and register the additional - * listeners with the CopyStreamAdapter. - *

- * The contents of the InputStream are - * read until the end of the stream is reached, but neither the - * source nor the destination are closed. You must do this yourself - * outside of the method call. The number of bytes read/written is - * returned. - * - * @param source The source InputStream. - * @param dest The destination OutputStream. - * @param bufferSize The number of bytes to buffer during the copy. - * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. - * @param streamSize The number of bytes in the stream being copied. - * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. - * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} - * @param listener The CopyStreamListener to notify of progress. If - * this parameter is null, notification is not attempted. - * @return number of bytes read/written - * @throws CopyStreamException If an error occurs while reading from the - * source or writing to the destination. The CopyStreamException - * will contain the number of bytes confirmed to have been - * transferred before an - * IOException occurred, and it will also contain the IOException - * that caused the error. These values can be retrieved with - * the CopyStreamException getTotalBytesTransferred() and - * getIOException() methods. - ***/ - public static final long copyStream(InputStream source, OutputStream dest, int bufferSize, - long streamSize, CopyStreamListener listener) throws CopyStreamException { - return copyStream(source, dest, bufferSize, streamSize, listener, true); - } - - /*** - * Copies the contents of an InputStream to an OutputStream using a - * copy buffer of a given size. The contents of the InputStream are - * read until the end of the stream is reached, but neither the - * source nor the destination are closed. You must do this yourself - * outside of the method call. The number of bytes read/written is - * returned. - * - * @param source The source InputStream. - * @param dest The destination OutputStream. - * @param bufferSize The number of bytes to buffer during the copy. - * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. - * @return The number of bytes read/written in the copy operation. - * @throws CopyStreamException If an error occurs while reading from the - * source or writing to the destination. The CopyStreamException - * will contain the number of bytes confirmed to have been - * transferred before an - * IOException occurred, and it will also contain the IOException - * that caused the error. These values can be retrieved with - * the CopyStreamException getTotalBytesTransferred() and - * getIOException() methods. - ***/ - public static final long copyStream(InputStream source, OutputStream dest, int bufferSize) - throws CopyStreamException { - return copyStream(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); - } - - /*** - * Same as copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); - * @param source where to copy from - * @param dest where to copy to - * @return number of bytes copied - * @throws CopyStreamException on error - ***/ - public static final long copyStream(InputStream source, OutputStream dest) - throws CopyStreamException { - return copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE); - } - - /*** - * Copies the contents of a Reader to a Writer using a - * copy buffer of a given size and notifies the provided - * CopyStreamListener of the progress of the copy operation by calling - * its bytesTransferred(long, int) method after each write to the - * destination. If you wish to notify more than one listener you should - * use a CopyStreamAdapter as the listener and register the additional - * listeners with the CopyStreamAdapter. - *

- * The contents of the Reader are - * read until its end is reached, but neither the source nor the - * destination are closed. You must do this yourself outside of the - * method call. The number of characters read/written is returned. - * - * @param source The source Reader. - * @param dest The destination writer. - * @param bufferSize The number of characters to buffer during the copy. - * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. - * @param streamSize The number of characters in the stream being copied. - * Should be set to CopyStreamEvent.UNKNOWN_STREAM_SIZE if unknown. - * Not currently used (though it is passed to {@link CopyStreamListener#bytesTransferred(long, int, long)} - * @param listener The CopyStreamListener to notify of progress. If - * this parameter is null, notification is not attempted. - * @return The number of characters read/written in the copy operation. - * @throws CopyStreamException If an error occurs while reading from the - * source or writing to the destination. The CopyStreamException - * will contain the number of bytes confirmed to have been - * transferred before an - * IOException occurred, and it will also contain the IOException - * that caused the error. These values can be retrieved with - * the CopyStreamException getTotalBytesTransferred() and - * getIOException() methods. - ***/ - public static final long copyReader(Reader source, Writer dest, int bufferSize, long streamSize, - CopyStreamListener listener) throws CopyStreamException { - int numChars; - long total = 0; - char[] buffer = new char[bufferSize > 0 ? bufferSize : DEFAULT_COPY_BUFFER_SIZE]; - - try { - while ((numChars = source.read(buffer)) != -1) { - // Technically, some read(char[]) methods may return 0 and we cannot - // accept that as an indication of EOF. - if (numChars == 0) { - int singleChar = source.read(); - if (singleChar < 0) { - break; - } - dest.write(singleChar); - dest.flush(); - ++total; - if (listener != null) { - listener.bytesTransferred(total, 1, streamSize); - } - continue; - } - - dest.write(buffer, 0, numChars); - dest.flush(); - total += numChars; - if (listener != null) { - listener.bytesTransferred(total, numChars, streamSize); - } - } - } catch (IOException e) { - throw new CopyStreamException("IOException caught while copying.", total, e); - } - - return total; - } - - /*** - * Copies the contents of a Reader to a Writer using a - * copy buffer of a given size. The contents of the Reader are - * read until its end is reached, but neither the source nor the - * destination are closed. You must do this yourself outside of the - * method call. The number of characters read/written is returned. - * - * @param source The source Reader. - * @param dest The destination writer. - * @param bufferSize The number of characters to buffer during the copy. - * A zero or negative value means to use {@link #DEFAULT_COPY_BUFFER_SIZE}. - * @return The number of characters read/written in the copy operation. - * @throws CopyStreamException If an error occurs while reading from the - * source or writing to the destination. The CopyStreamException - * will contain the number of bytes confirmed to have been - * transferred before an - * IOException occurred, and it will also contain the IOException - * that caused the error. These values can be retrieved with - * the CopyStreamException getTotalBytesTransferred() and - * getIOException() methods. - ***/ - public static final long copyReader(Reader source, Writer dest, int bufferSize) - throws CopyStreamException { - return copyReader(source, dest, bufferSize, CopyStreamEvent.UNKNOWN_STREAM_SIZE, null); - } - - /*** - * Same as copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); - * @param source where to copy from - * @param dest where to copy to - * @return number of bytes copied - * @throws CopyStreamException on error - ***/ - public static final long copyReader(Reader source, Writer dest) throws CopyStreamException { - return copyReader(source, dest, DEFAULT_COPY_BUFFER_SIZE); - } - - /** - * Closes the object quietly, catching rather than throwing IOException. - * Intended for use from finally blocks. - * - * @param closeable the object to close, may be {@code null} - * @since 3.0 - */ - public static void closeQuietly(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException e) { - // Ignored - } - } - } - - /** - * Closes the socket quietly, catching rather than throwing IOException. - * Intended for use from finally blocks. - * - * @param socket the socket to close, may be {@code null} - * @since 3.0 - */ - public static void closeQuietly(Socket socket) { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - // Ignored - } - } - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/package-info.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/package-info.java deleted file mode 100644 index 06207d12..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/io/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Utility classes for IO support. - */ -package aria.apache.commons.net.io; \ No newline at end of file diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Base64.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Base64.java deleted file mode 100644 index a03150e0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Base64.java +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.util; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; - -/** - * Provides Base64 encoding and decoding as defined by RFC 2045. - * - *

- * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 - * Multipurpose - * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and - * Borenstein. - *

- *

- * The class can be parameterized in the following manner with various constructors: - *

    - *
  • URL-safe mode: Default off.
  • - *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up - * being multiples of - * 4 in the encoded data. - *
  • Line separator: Default is CRLF ("\r\n")
  • - *
- *

- * Since this class operates directly on byte streams, and not character streams, it is hard-coded - * to only encode/decode - * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, - * Windows-1252, UTF-8, etc). - *

- * - * @version $Id: Base64.java 1697293 2015-08-24 01:01:00Z sebb $ - * @see RFC 2045 - * @since 2.2 - */ -public class Base64 { - private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; - - private static final int DEFAULT_BUFFER_SIZE = 8192; - - /** - * Chunk size per RFC 2045 section 6.8. - * - *

- * The {@value} character limit does not count the trailing CRLF, but counts all other characters, - * including any - * equal signs. - *

- * - * @see RFC 2045 section 6.8 - */ - static final int CHUNK_SIZE = 76; - - /** - * Chunk separator per RFC 2045 section 2.1. - * - * @see RFC 2045 section 2.1 - */ - private static final byte[] CHUNK_SEPARATOR = { '\r', '\n' }; - - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their - * "Base64 Alphabet" - * equivalents as specified in Table 1 of RFC 2045. - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] STANDARD_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / - * changed to - and _ to make the encoded Base64 results more URL-SAFE. - * This table is only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', '-', '_' - }; - - /** - * Byte used to pad output. - */ - private static final byte PAD = '='; - - /** - * This array is a lookup table that translates Unicode characters drawn from the "Base64 - * Alphabet" (as specified in - * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in - * the Base64 - * alphabet but fall within the bounds of the array are translated to -1. - * - * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder - * seamlessly handles both - * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what - * to emit). - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] DECODE_TABLE = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, - -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, - 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, 51 - }; - - /** Mask used to extract 6 bits, used when encoding */ - private static final int MASK_6BITS = 0x3f; - - /** Mask used to extract 8 bits, used in decoding base64 bytes */ - private static final int MASK_8BITS = 0xff; - - // The static final fields above are used for the original static byte[] methods on Base64. - // The private member fields below are used with the new streaming approach, which requires - // some state be preserved between calls of encode() and decode(). - - /** - * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static - * because it is able - * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so - * we can switch - * between the two modes. - */ - private final byte[] encodeTable; - - /** - * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking - * of the base64 - * encoded data. - */ - private final int lineLength; - - /** - * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. - */ - private final byte[] lineSeparator; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs - * resizing. - * decodeSize = 3 + lineSeparator.length; - */ - private final int decodeSize; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs - * resizing. - * encodeSize = 4 + lineSeparator.length; - */ - private final int encodeSize; - - /** - * Buffer for streaming. - */ - private byte[] buffer; - - /** - * Position where next character should be written in the buffer. - */ - private int pos; - - /** - * Position where next character should be read from the buffer. - */ - private int readPos; - - /** - * Variable tracks how many characters have been written to the current line. Only used when - * encoding. We use it to - * make sure each encoded line never goes beyond lineLength (if lineLength > 0). - */ - private int currentLinePos; - - /** - * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when - * decoding. This variable - * helps track that. - */ - private int modulus; - - /** - * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 - * object becomes useless, - * and must be thrown away. - */ - private boolean eof; - - /** - * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store - * and extract the - * base64 encoding or decoding from this variable. - */ - private int x; - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- */ - public Base64() { - this(false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- * - * @param urlSafe if true, URL-safe encoding is used. In most cases this should be - * set to - * false. - * @since 1.4 - */ - public Base64(boolean urlSafe) { - this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is given in the constructor, the line separator is CRLF, and the - * encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in - * the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given length (rounded down - * to nearest multiple of 4). - * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored - * when decoding. - * @since 1.4 - */ - public Base64(int lineLength) { - this(lineLength, CHUNK_SEPARATOR); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding - * table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in - * the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given length (rounded down - * to nearest multiple of 4). - * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored - * when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence of bytes. - * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 - * characters. - * @since 1.4 - */ - public Base64(int lineLength, byte[] lineSeparator) { - this(lineLength, lineSeparator, false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding - * table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in - * the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength Each line of encoded data will be at most of the given length (rounded down - * to nearest multiple of 4). - * If {@code lineLength <= 0}, then the output will not be divided into lines (chunks). Ignored - * when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence of bytes. - * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is - * only applied to encode - * operations. Decoding seamlessly handles both modes. - * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. - * That's not going to work! - * @since 1.4 - */ - public Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { - if (lineSeparator == null) { - lineLength = 0; // disable chunk-separating - lineSeparator = EMPTY_BYTE_ARRAY; // this just gets ignored - } - this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0; - this.lineSeparator = new byte[lineSeparator.length]; - System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); - if (lineLength > 0) { - this.encodeSize = 4 + lineSeparator.length; - } else { - this.encodeSize = 4; - } - this.decodeSize = this.encodeSize - 1; - if (containsBase64Byte(lineSeparator)) { - String sep = newStringUtf8(lineSeparator); - throw new IllegalArgumentException( - "lineSeperator must not contain base64 characters: [" + sep + "]"); - } - this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; - } - - /** - * Returns our current encode mode. True if we're URL-SAFE, false otherwise. - * - * @return true if we're in URL-SAFE mode, false otherwise. - * @since 1.4 - */ - public boolean isUrlSafe() { - return this.encodeTable == URL_SAFE_ENCODE_TABLE; - } - - /** - * Returns true if this Base64 object has buffered data for reading. - * - * @return true if there is Base64 object still available for reading. - */ - boolean hasData() { - return this.buffer != null; - } - - /** - * Returns the amount of buffered data available for reading. - * - * @return The amount of buffered data available for reading. - */ - int avail() { - return buffer != null ? pos - readPos : 0; - } - - /** Doubles our buffer. */ - private void resizeBuffer() { - if (buffer == null) { - buffer = new byte[DEFAULT_BUFFER_SIZE]; - pos = 0; - readPos = 0; - } else { - byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; - System.arraycopy(buffer, 0, b, 0, buffer.length); - buffer = b; - } - } - - /** - * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a - * maximum of bAvail - * bytes. Returns how many bytes were actually extracted. - * - * @param b byte[] array to extract the buffered data into. - * @param bPos position in byte[] array to start extraction at. - * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are - * available). - * @return The number of bytes successfully extracted into the provided byte[] array. - */ - int readResults(byte[] b, int bPos, int bAvail) { - if (buffer != null) { - int len = Math.min(avail(), bAvail); - if (buffer != b) { - System.arraycopy(buffer, readPos, b, bPos, len); - readPos += len; - if (readPos >= pos) { - buffer = null; - } - } else { - // Re-using the original consumer's output array is only - // allowed for one round. - buffer = null; - } - return len; - } - return eof ? -1 : 0; - } - - /** - * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the - * consumer's output - * array for one round (if the consumer calls this method first) instead of starting our own - * buffer. - * - * @param out byte[] array to buffer directly to. - * @param outPos Position to start buffering into. - * @param outAvail Amount of bytes available for direct buffering. - */ - void setInitialBuffer(byte[] out, int outPos, int outAvail) { - // We can re-use consumer's original output array under - // special circumstances, saving on some System.arraycopy(). - if (out != null && out.length == outAvail) { - buffer = out; - pos = outPos; - readPos = outPos; - } - } - - /** - *

- * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least - * twice: once with - * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been - * reached, so flush last - * remaining bytes (if not multiple of 3). - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in byte[] array of binary data to base64 encode. - * @param inPos Position to start reading data from. - * @param inAvail Amount of bytes available from input for encoding. - */ - void encode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - // inAvail < 0 is how we're informed of EOF in the underlying data we're - // encoding. - if (inAvail < 0) { - eof = true; - if (buffer == null || buffer.length - pos < encodeSize) { - resizeBuffer(); - } - switch (modulus) { - case 1: - buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - buffer[pos++] = PAD; - } - break; - - case 2: - buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[pos++] = PAD; - } - break; - default: - break; // other values ignored - } - if (lineLength > 0 && pos > 0) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - } - } else { - for (int i = 0; i < inAvail; i++) { - if (buffer == null || buffer.length - pos < encodeSize) { - resizeBuffer(); - } - modulus = (++modulus) % 3; - int b = in[inPos++]; - if (b < 0) { - b += 256; - } - x = (x << 8) + b; - if (0 == modulus) { - buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS]; - buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS]; - buffer[pos++] = encodeTable[x & MASK_6BITS]; - currentLinePos += 4; - if (lineLength > 0 && lineLength <= currentLinePos) { - System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); - pos += lineSeparator.length; - currentLinePos = 0; - } - } - } - } - } - - /** - *

- * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at - * least twice: once - * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been - * reached. The "-1" - * call is not necessary when decoding, but it doesn't hurt, either. - *

- *

- * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, - * since CR and LF are - * silently ignored, but has implications for other bytes, too. This method subscribes to the - * garbage-in, - * garbage-out philosophy: it will not check the provided data for validity. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in byte[] array of ascii data to base64 decode. - * @param inPos Position to start reading data from. - * @param inAvail Amount of bytes available from input for encoding. - */ - void decode(byte[] in, int inPos, int inAvail) { - if (eof) { - return; - } - if (inAvail < 0) { - eof = true; - } - for (int i = 0; i < inAvail; i++) { - if (buffer == null || buffer.length - pos < decodeSize) { - resizeBuffer(); - } - byte b = in[inPos++]; - if (b == PAD) { - // We're done. - eof = true; - break; - } else { - if (b >= 0 && b < DECODE_TABLE.length) { - int result = DECODE_TABLE[b]; - if (result >= 0) { - modulus = (++modulus) % 4; - x = (x << 6) + result; - if (modulus == 0) { - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); - buffer[pos++] = (byte) (x & MASK_8BITS); - } - } - } - } - } - - // Two forms of EOF as far as base64 decoder is concerned: actual - // EOF (-1) and first time '=' character is encountered in stream. - // This approach makes the '=' padding characters completely optional. - if (eof && modulus != 0) { - x = x << 6; - switch (modulus) { - case 2: - x = x << 6; - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - break; - case 3: - buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS); - buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS); - break; - default: - break; // other values ignored - } - } - } - - /** - * Returns whether or not the octet is in the base 64 alphabet. - * - * @param octet The value to test - * @return true if the value is defined in the the base 64 alphabet, - * false otherwise. - * @since 1.4 - */ - public static boolean isBase64(byte octet) { - return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 - * alphabet. Currently the - * method treats whitespace as valid. - * - * @param arrayOctet byte array to test - * @return true if all bytes are valid characters in the Base64 alphabet or if the - * byte array is empty; - * false, otherwise - */ - public static boolean isArrayByteBase64(byte[] arrayOctet) { - for (int i = 0; i < arrayOctet.length; i++) { - if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { - return false; - } - } - return true; - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 - * alphabet. - * - * @param arrayOctet byte array to test - * @return true if any byte is a valid character in the Base64 alphabet; false - * herwise - */ - private static boolean containsBase64Byte(byte[] arrayOctet) { - for (byte element : arrayOctet) { - if (isBase64(element)) { - return true; - } - } - return false; - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * @param binaryData binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - */ - public static byte[] encodeBase64(byte[] binaryData) { - return encodeBase64(binaryData, false); - } - - /** - * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF. - *

- * For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}. - * - * @param binaryData binary data to encode - * @return String containing Base64 characters. - * @since 1.4 - */ - public static String encodeBase64String(byte[] binaryData) { - return newStringUtf8(encodeBase64(binaryData, true)); - } - - /** - * Encodes binary data using the base64 algorithm, without using chunking. - *

- * For a chunking version, see {@link #encodeBase64String(byte[])}. - * - * @param binaryData binary data to encode - * @return String containing Base64 characters. - * @since 3.2 - */ - public static String encodeBase64StringUnChunked(byte[] binaryData) { - return newStringUtf8(encodeBase64(binaryData, false)); - } - - /** - * Encodes binary data using the base64 algorithm. - * - * @param binaryData binary data to encode - * @param useChunking whether to split the output into chunks - * @return String containing Base64 characters. - * @since 3.2 - */ - public static String encodeBase64String(byte[] binaryData, boolean useChunking) { - return newStringUtf8(encodeBase64(binaryData, useChunking)); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the - * output. The - * url-safe variation emits - and _ instead of + and / characters. - * - * @param binaryData binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - * @since 1.4 - */ - public static byte[] encodeBase64URLSafe(byte[] binaryData) { - return encodeBase64(binaryData, false, true); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the - * output. The - * url-safe variation emits - and _ instead of + and / characters. - * - * @param binaryData binary data to encode - * @return String containing Base64 characters - * @since 1.4 - */ - public static String encodeBase64URLSafeString(byte[] binaryData) { - return newStringUtf8(encodeBase64(binaryData, false, true)); - } - - /** - * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character - * blocks - * - * @param binaryData binary data to encode - * @return Base64 characters chunked in 76 character blocks - */ - public static byte[] encodeBase64Chunked(byte[] binaryData) { - return encodeBase64(binaryData, true); - } - - /** - * Decodes a String containing containing characters in the Base64 alphabet. - * - * @param pArray A String containing Base64 character data - * @return a byte array containing binary data - * @since 1.4 - */ - public byte[] decode(String pArray) { - return decode(getBytesUtf8(pArray)); - } - - private byte[] getBytesUtf8(String pArray) { - try { - return pArray.getBytes("UTF8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - /** - * Decodes a byte[] containing containing characters in the Base64 alphabet. - * - * @param pArray A byte array containing Base64 character data - * @return a byte array containing binary data - */ - public byte[] decode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - long len = (pArray.length * 3) / 4; - byte[] buf = new byte[(int) len]; - setInitialBuffer(buf, 0, buf.length); - decode(pArray, 0, pArray.length); - decode(pArray, 0, -1); // Notify decoder of EOF. - - // Would be nice to just return buf (like we sometimes do in the encode - // logic), but we have no idea what the line-length was (could even be - // variable). So we cannot determine ahead of time exactly how big an - // array is necessary. Hence the need to construct a 2nd byte array to - // hold the final result: - - byte[] result = new byte[pos]; - readResults(result, 0, result.length); - return result; - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 - * character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if true this encoder will chunk the base64 output into 76 - * character blocks - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than - * {@link Integer#MAX_VALUE} - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { - return encodeBase64(binaryData, isChunked, false); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 - * character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if true this encoder will chunk the base64 output into 76 - * character blocks - * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / - * characters. - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than - * {@link Integer#MAX_VALUE} - * @since 1.4 - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe) { - return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 - * character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if true this encoder will chunk the base64 output into 76 - * character blocks - * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / - * characters. - * @param maxResultSize The maximum result size to accept. - * @return Base64-encoded data. - * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than - * maxResultSize - * @since 1.4 - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, - int maxResultSize) { - if (binaryData == null || binaryData.length == 0) { - return binaryData; - } - - long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, - isChunked ? CHUNK_SEPARATOR : EMPTY_BYTE_ARRAY); - if (len > maxResultSize) { - throw new IllegalArgumentException("Input array too big, the output array would be bigger (" - + len - + ") than the specified maxium size of " - + maxResultSize); - } - - Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); - return b64.encode(binaryData); - } - - /** - * Decodes a Base64 String into octets - * - * @param base64String String containing Base64 data - * @return Array containing decoded data. - * @since 1.4 - */ - public static byte[] decodeBase64(String base64String) { - return new Base64().decode(base64String); - } - - /** - * Decodes Base64 data into octets - * - * @param base64Data Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(byte[] base64Data) { - return new Base64().decode(base64Data); - } - - /** - * Checks if a byte value is whitespace or not. - * - * @param byteToCheck the byte to check - * @return true if byte is whitespace, false otherwise - */ - private static boolean isWhiteSpace(byte byteToCheck) { - switch (byteToCheck) { - case ' ': - case '\n': - case '\r': - case '\t': - return true; - default: - return false; - } - } - - /** - * Encodes a byte[] containing binary data, into a String containing characters in the Base64 - * alphabet. - * - * @param pArray a byte array containing binary data - * @return A String containing only Base64 character data - * @since 1.4 - */ - public String encodeToString(byte[] pArray) { - return newStringUtf8(encode(pArray)); - } - - private static String newStringUtf8(byte[] encode) { - String str = null; - try { - str = new String(encode, "UTF8"); - } catch (UnsupportedEncodingException ue) { - throw new RuntimeException(ue); - } - return str; - } - - /** - * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 - * alphabet. - * - * @param pArray a byte array containing binary data - * @return A byte array containing only Base64 character data - */ - public byte[] encode(byte[] pArray) { - reset(); - if (pArray == null || pArray.length == 0) { - return pArray; - } - long len = getEncodeLength(pArray, lineLength, lineSeparator); - byte[] buf = new byte[(int) len]; - setInitialBuffer(buf, 0, buf.length); - encode(pArray, 0, pArray.length); - encode(pArray, 0, -1); // Notify encoder of EOF. - // Encoder might have resized, even though it was unnecessary. - if (buffer != buf) { - readResults(buf, 0, buf.length); - } - // In URL-SAFE mode we skip the padding characters, so sometimes our - // final length is a bit smaller. - if (isUrlSafe() && pos < buf.length) { - byte[] smallerBuf = new byte[pos]; - System.arraycopy(buf, 0, smallerBuf, 0, pos); - buf = smallerBuf; - } - return buf; - } - - /** - * Pre-calculates the amount of space needed to base64-encode the supplied array. - * - * @param pArray byte[] array which will later be encoded - * @param chunkSize line-length of the output (<= 0 means no chunking) between each - * chunkSeparator (e.g. CRLF). - * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF). - * @return amount of space needed to encoded the supplied array. Returns - * a long since a max-len array will require Integer.MAX_VALUE + 33%. - */ - private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) { - // base64 always encodes to multiples of 4. - chunkSize = (chunkSize / 4) * 4; - - long len = (pArray.length * 4) / 3; - long mod = len % 4; - if (mod != 0) { - len += 4 - mod; - } - if (chunkSize > 0) { - boolean lenChunksPerfectly = len % chunkSize == 0; - len += (len / chunkSize) * chunkSeparator.length; - if (!lenChunksPerfectly) { - len += chunkSeparator.length; - } - } - return len; - } - - // Implementation of integer encoding used for crypto - - /** - * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature - * - * @param pArray a byte array containing base64 character data - * @return A BigInteger - * @since 1.4 - */ - public static BigInteger decodeInteger(byte[] pArray) { - return new BigInteger(1, decodeBase64(pArray)); - } - - /** - * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature - * - * @param bigInt a BigInteger - * @return A byte array containing base64 character data - * @throws NullPointerException if null is passed in - * @since 1.4 - */ - public static byte[] encodeInteger(BigInteger bigInt) { - if (bigInt == null) { - throw new NullPointerException("encodeInteger called with null parameter"); - } - return encodeBase64(toIntegerBytes(bigInt), false); - } - - /** - * Returns a byte-array representation of a BigInteger without sign bit. - * - * @param bigInt BigInteger to be converted - * @return a byte array representation of the BigInteger parameter - */ - static byte[] toIntegerBytes(BigInteger bigInt) { - int bitlen = bigInt.bitLength(); - // round bitlen - bitlen = ((bitlen + 7) >> 3) << 3; - byte[] bigBytes = bigInt.toByteArray(); - - if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { - return bigBytes; - } - // set up params for copying everything but sign bit - int startSrc = 0; - int len = bigBytes.length; - - // if bigInt is exactly byte-aligned, just skip signbit in copy - if ((bigInt.bitLength() % 8) == 0) { - startSrc = 1; - len--; - } - int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec - byte[] resizedBytes = new byte[bitlen / 8]; - System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); - return resizedBytes; - } - - /** - * Resets this Base64 object to its initial newly constructed state. - */ - private void reset() { - buffer = null; - pos = 0; - readPos = 0; - currentLinePos = 0; - modulus = 0; - eof = false; - } - - // Getters for use in testing - - int getLineLength() { - return lineLength; - } - - byte[] getLineSeparator() { - return lineSeparator.clone(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Charsets.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Charsets.java deleted file mode 100644 index 5001fcc0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/Charsets.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.util; - -import java.nio.charset.Charset; - -/** - * Helps dealing with Charsets. - * - * @since 3.3 - */ -public class Charsets { - - /** - * Returns a charset object for the given charset name. - * - * @param charsetName The name of the requested charset; may be a canonical name, an alias, or - * null. If null, return the - * default charset. - * @return A charset object for the named charset - */ - public static Charset toCharset(String charsetName) { - return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName); - } - - /** - * Returns a charset object for the given charset name. - * - * @param charsetName The name of the requested charset; may be a canonical name, an alias, or - * null. - * If null, return the default charset. - * @param defaultCharsetName the charset name to use if the requested charset is null - * @return A charset object for the named charset - */ - public static Charset toCharset(String charsetName, String defaultCharsetName) { - return charsetName == null ? Charset.forName(defaultCharsetName) : Charset.forName(charsetName); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java deleted file mode 100644 index 012493b0..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/KeyManagerUtils.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.util; - -import aria.apache.commons.net.io.Util; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.Socket; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.util.Enumeration; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.X509ExtendedKeyManager; - -/** - * General KeyManager utilities - *

- * How to use with a client certificate: - *

- * KeyManager km = KeyManagerUtils.createClientKeyManager("JKS",
- *     "/path/to/privatekeystore.jks","storepassword",
- *     "privatekeyalias", "keypassword");
- * FTPSClient cl = new FTPSClient();
- * cl.setKeyManager(km);
- * cl.connect(...);
- * 
- * If using the default store type and the key password is the same as the - * store password, these parameters can be omitted.
- * If the desired key is the first or only key in the keystore, the keyAlias parameter - * can be omitted, in which case the code becomes: - *
- * KeyManager km = KeyManagerUtils.createClientKeyManager(
- *     "/path/to/privatekeystore.jks","storepassword");
- * FTPSClient cl = new FTPSClient();
- * cl.setKeyManager(km);
- * cl.connect(...);
- * 
- * - * @since 3.0 - */ -public final class KeyManagerUtils { - - private static final String DEFAULT_STORE_TYPE = KeyStore.getDefaultType(); - - private KeyManagerUtils() { - // Not instantiable - } - - /** - * Create a client key manager which returns a particular key. - * Does not handle server keys. - * - * @param ks the keystore to use - * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key - * entry alias is used - * @param keyPass the password of the key to use - * @return the customised KeyManager - * @throws GeneralSecurityException if there is a problem creating the keystore - */ - public static KeyManager createClientKeyManager(KeyStore ks, String keyAlias, String keyPass) - throws GeneralSecurityException { - ClientKeyStore cks = - new ClientKeyStore(ks, keyAlias != null ? keyAlias : findAlias(ks), keyPass); - return new X509KeyManager(cks); - } - - /** - * Create a client key manager which returns a particular key. - * Does not handle server keys. - * - * @param storeType the type of the keyStore, e.g. "JKS" - * @param storePath the path to the keyStore - * @param storePass the keyStore password - * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key - * entry alias is used - * @param keyPass the password of the key to use - * @return the customised KeyManager - * @throws GeneralSecurityException if there is a problem creating the keystore - * @throws IOException if there is a problem creating the keystore - */ - public static KeyManager createClientKeyManager(String storeType, File storePath, - String storePass, String keyAlias, String keyPass) - throws IOException, GeneralSecurityException { - KeyStore ks = loadStore(storeType, storePath, storePass); - return createClientKeyManager(ks, keyAlias, keyPass); - } - - /** - * Create a client key manager which returns a particular key. - * Does not handle server keys. - * Uses the default store type and assumes the key password is the same as the store password - * - * @param storePath the path to the keyStore - * @param storePass the keyStore password - * @param keyAlias the alias of the key to use, may be {@code null} in which case the first key - * entry alias is used - * @return the customised KeyManager - * @throws IOException if there is a problem creating the keystore - * @throws GeneralSecurityException if there is a problem creating the keystore - */ - public static KeyManager createClientKeyManager(File storePath, String storePass, String keyAlias) - throws IOException, GeneralSecurityException { - return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, keyAlias, storePass); - } - - /** - * Create a client key manager which returns a particular key. - * Does not handle server keys. - * Uses the default store type and assumes the key password is the same as the store password. - * The key alias is found by searching the keystore for the first private key entry - * - * @param storePath the path to the keyStore - * @param storePass the keyStore password - * @return the customised KeyManager - * @throws IOException if there is a problem creating the keystore - * @throws GeneralSecurityException if there is a problem creating the keystore - */ - public static KeyManager createClientKeyManager(File storePath, String storePass) - throws IOException, GeneralSecurityException { - return createClientKeyManager(DEFAULT_STORE_TYPE, storePath, storePass, null, storePass); - } - - private static KeyStore loadStore(String storeType, File storePath, String storePass) - throws KeyStoreException, IOException, GeneralSecurityException { - KeyStore ks = KeyStore.getInstance(storeType); - FileInputStream stream = null; - try { - stream = new FileInputStream(storePath); - ks.load(stream, storePass.toCharArray()); - } finally { - Util.closeQuietly(stream); - } - return ks; - } - - private static String findAlias(KeyStore ks) throws KeyStoreException { - Enumeration e = ks.aliases(); - while (e.hasMoreElements()) { - String entry = e.nextElement(); - if (ks.isKeyEntry(entry)) { - return entry; - } - } - throw new KeyStoreException("Cannot find a private key entry"); - } - - private static class ClientKeyStore { - - private final X509Certificate[] certChain; - private final PrivateKey key; - private final String keyAlias; - - ClientKeyStore(KeyStore ks, String keyAlias, String keyPass) throws GeneralSecurityException { - this.keyAlias = keyAlias; - this.key = (PrivateKey) ks.getKey(this.keyAlias, keyPass.toCharArray()); - Certificate[] certs = ks.getCertificateChain(this.keyAlias); - X509Certificate[] X509certs = new X509Certificate[certs.length]; - for (int i = 0; i < certs.length; i++) { - X509certs[i] = (X509Certificate) certs[i]; - } - this.certChain = X509certs; - } - - final X509Certificate[] getCertificateChain() { - return this.certChain; - } - - final PrivateKey getPrivateKey() { - return this.key; - } - - final String getAlias() { - return this.keyAlias; - } - } - - private static class X509KeyManager extends X509ExtendedKeyManager { - - private final ClientKeyStore keyStore; - - X509KeyManager(final ClientKeyStore keyStore) { - this.keyStore = keyStore; - } - - // Call sequence: 1 - @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, - Socket socket) { - return keyStore.getAlias(); - } - - // Call sequence: 2 - @Override public X509Certificate[] getCertificateChain(String alias) { - return keyStore.getCertificateChain(); - } - - @Override public String[] getClientAliases(String keyType, Principal[] issuers) { - return new String[] { keyStore.getAlias() }; - } - - // Call sequence: 3 - @Override public PrivateKey getPrivateKey(String alias) { - return keyStore.getPrivateKey(); - } - - @Override public String[] getServerAliases(String keyType, Principal[] issuers) { - return null; - } - - @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { - return null; - } - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/ListenerList.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/ListenerList.java deleted file mode 100644 index 2ddfa637..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/ListenerList.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.util; - -import java.io.Serializable; -import java.util.EventListener; -import java.util.Iterator; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - */ - -public class ListenerList implements Serializable, Iterable { - private static final long serialVersionUID = -1934227607974228213L; - - private final CopyOnWriteArrayList __listeners; - - public ListenerList() { - __listeners = new CopyOnWriteArrayList(); - } - - public void addListener(EventListener listener) { - __listeners.add(listener); - } - - public void removeListener(EventListener listener) { - __listeners.remove(listener); - } - - public int getListenerCount() { - return __listeners.size(); - } - - /** - * Return an {@link Iterator} for the {@link EventListener} instances. - * - * @return an {@link Iterator} for the {@link EventListener} instances - * @since 2.0 - * TODO Check that this is a good defensive strategy - */ - @Override public Iterator iterator() { - return __listeners.iterator(); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java deleted file mode 100644 index ddec2139..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLContextUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.util; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -/** - * General utilities for SSLContext. - * - * @since 3.0 - */ -public class SSLContextUtils { - - private SSLContextUtils() { - // Not instantiable - } - - /** - * Create and initialise an SSLContext. - * - * @param protocol the protocol used to instatiate the context - * @param keyManager the key manager, may be {@code null} - * @param trustManager the trust manager, may be {@code null} - * @return the initialised context. - * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs - */ - public static SSLContext createSSLContext(String protocol, KeyManager keyManager, - TrustManager trustManager) throws IOException { - return createSSLContext(protocol, keyManager == null ? null : new KeyManager[] { keyManager }, - trustManager == null ? null : new TrustManager[] { trustManager }); - } - - /** - * Create and initialise an SSLContext. - * - * @param protocol the protocol used to instatiate the context - * @param keyManagers the array of key managers, may be {@code null} but array entries must not be - * {@code null} - * @param trustManagers the array of trust managers, may be {@code null} but array entries must - * not be {@code null} - * @return the initialised context. - * @throws IOException this is used to wrap any {@link GeneralSecurityException} that occurs - */ - public static SSLContext createSSLContext(String protocol, KeyManager[] keyManagers, - TrustManager[] trustManagers) throws IOException { - SSLContext ctx; - try { - ctx = SSLContext.getInstance(protocol); - ctx.init(keyManagers, trustManagers, /*SecureRandom*/ null); - } catch (GeneralSecurityException e) { - IOException ioe = new IOException("Could not initialize SSL context"); - ioe.initCause(e); - throw ioe; - } - return ctx; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java deleted file mode 100644 index 24f335ed..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SSLSocketUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package aria.apache.commons.net.util; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.net.ssl.SSLSocket; - -/** - * General utilities for SSLSocket. - * - * @since 3.4 - */ -public class SSLSocketUtils { - private SSLSocketUtils() { - // Not instantiable - } - - /** - * Enable the HTTPS endpoint identification algorithm on an SSLSocket. - * - * @param socket the SSL socket - * @return {@code true} on success (this is only supported on Java 1.7+) - */ - public static boolean enableEndpointNameVerification(SSLSocket socket) { - try { - Class cls = Class.forName("javax.net.ssl.SSLParameters"); - Method setEndpointIdentificationAlgorithm = - cls.getDeclaredMethod("setEndpointIdentificationAlgorithm", String.class); - Method getSSLParameters = SSLSocket.class.getDeclaredMethod("getSSLParameters"); - Method setSSLParameters = SSLSocket.class.getDeclaredMethod("setSSLParameters", cls); - if (setEndpointIdentificationAlgorithm != null - && getSSLParameters != null - && setSSLParameters != null) { - Object sslParams = getSSLParameters.invoke(socket); - if (sslParams != null) { - setEndpointIdentificationAlgorithm.invoke(sslParams, "HTTPS"); - setSSLParameters.invoke(socket, sslParams); - return true; - } - } - } catch (SecurityException e) { // Ignored - } catch (ClassNotFoundException e) { // Ignored - } catch (NoSuchMethodException e) { // Ignored - } catch (IllegalArgumentException e) { // Ignored - } catch (IllegalAccessException e) { // Ignored - } catch (InvocationTargetException e) { // Ignored - } - return false; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SubnetUtils.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SubnetUtils.java deleted file mode 100644 index 9dd2e22e..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/SubnetUtils.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package aria.apache.commons.net.util; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A class that performs some subnet calculations given a network address and a subnet mask. - * - * @see "http://www.faqs.org/rfcs/rfc1519.html" - * @since 2.0 - */ -public class SubnetUtils { - - private static final String IP_ADDRESS = "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})"; - private static final String SLASH_FORMAT = IP_ADDRESS + "/(\\d{1,3})"; - private static final Pattern addressPattern = Pattern.compile(IP_ADDRESS); - private static final Pattern cidrPattern = Pattern.compile(SLASH_FORMAT); - private static final int NBITS = 32; - - private int netmask = 0; - private int address = 0; - private int network = 0; - private int broadcast = 0; - - /** Whether the broadcast/network address are included in host count */ - private boolean inclusiveHostCount = false; - - /** - * Constructor that takes a CIDR-notation string, e.g. "192.168.0.1/16" - * - * @param cidrNotation A CIDR-notation string, e.g. "192.168.0.1/16" - * @throws IllegalArgumentException if the parameter is invalid, - * i.e. does not match n.n.n.n/m where n=1-3 decimal digits, m = 1-3 decimal digits in range 1-32 - */ - public SubnetUtils(String cidrNotation) { - calculate(cidrNotation); - } - - /** - * Constructor that takes a dotted decimal address and a dotted decimal mask. - * - * @param address An IP address, e.g. "192.168.0.1" - * @param mask A dotted decimal netmask e.g. "255.255.0.0" - * @throws IllegalArgumentException if the address or mask is invalid, - * i.e. does not match n.n.n.n where n=1-3 decimal digits and the mask is not all zeros - */ - public SubnetUtils(String address, String mask) { - calculate(toCidrNotation(address, mask)); - } - - /** - * Returns true if the return value of {@link SubnetInfo#getAddressCount()} - * includes the network and broadcast addresses. - * - * @return true if the hostcount includes the network and broadcast addresses - * @since 2.2 - */ - public boolean isInclusiveHostCount() { - return inclusiveHostCount; - } - - /** - * Set to true if you want the return value of {@link SubnetInfo#getAddressCount()} - * to include the network and broadcast addresses. - * - * @param inclusiveHostCount true if network and broadcast addresses are to be included - * @since 2.2 - */ - public void setInclusiveHostCount(boolean inclusiveHostCount) { - this.inclusiveHostCount = inclusiveHostCount; - } - - /** - * Convenience container for subnet summary information. - */ - public final class SubnetInfo { - /* Mask to convert unsigned int to a long (i.e. keep 32 bits) */ - private static final long UNSIGNED_INT_MASK = 0x0FFFFFFFFL; - - private SubnetInfo() { - } - - private int netmask() { - return netmask; - } - - private int network() { - return network; - } - - private int address() { - return address; - } - - private int broadcast() { - return broadcast; - } - - // long versions of the values (as unsigned int) which are more suitable for range checking - private long networkLong() { - return network & UNSIGNED_INT_MASK; - } - - private long broadcastLong() { - return broadcast & UNSIGNED_INT_MASK; - } - - private int low() { - return (isInclusiveHostCount() ? network() - : broadcastLong() - networkLong() > 1 ? network() + 1 : 0); - } - - private int high() { - return (isInclusiveHostCount() ? broadcast() - : broadcastLong() - networkLong() > 1 ? broadcast() - 1 : 0); - } - - /** - * Returns true if the parameter address is in the - * range of usable endpoint addresses for this subnet. This excludes the - * network and broadcast adresses. - * - * @param address A dot-delimited IPv4 address, e.g. "192.168.0.1" - * @return True if in range, false otherwise - */ - public boolean isInRange(String address) { - return isInRange(toInteger(address)); - } - - /** - * @param address the address to check - * @return true if it is in range - * @since 3.4 (made public) - */ - public boolean isInRange(int address) { - long addLong = address & UNSIGNED_INT_MASK; - long lowLong = low() & UNSIGNED_INT_MASK; - long highLong = high() & UNSIGNED_INT_MASK; - return addLong >= lowLong && addLong <= highLong; - } - - public String getBroadcastAddress() { - return format(toArray(broadcast())); - } - - public String getNetworkAddress() { - return format(toArray(network())); - } - - public String getNetmask() { - return format(toArray(netmask())); - } - - public String getAddress() { - return format(toArray(address())); - } - - /** - * Return the low address as a dotted IP address. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address - */ - public String getLowAddress() { - return format(toArray(low())); - } - - /** - * Return the high address as a dotted IP address. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the IP address in dotted format, may be "0.0.0.0" if there is no valid address - */ - public String getHighAddress() { - return format(toArray(high())); - } - - /** - * Get the count of available addresses. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the count of addresses, may be zero. - * @throws RuntimeException if the correct count is greater than {@code Integer.MAX_VALUE} - * @deprecated (3.4) use {@link #getAddressCountLong()} instead - */ - @Deprecated public int getAddressCount() { - long countLong = getAddressCountLong(); - if (countLong > Integer.MAX_VALUE) { - throw new RuntimeException("Count is larger than an integer: " + countLong); - } - // N.B. cannot be negative - return (int) countLong; - } - - /** - * Get the count of available addresses. - * Will be zero for CIDR/31 and CIDR/32 if the inclusive flag is false. - * - * @return the count of addresses, may be zero. - * @since 3.4 - */ - public long getAddressCountLong() { - long b = broadcastLong(); - long n = networkLong(); - long count = b - n + (isInclusiveHostCount() ? 1 : -1); - return count < 0 ? 0 : count; - } - - public int asInteger(String address) { - return toInteger(address); - } - - public String getCidrSignature() { - return toCidrNotation(format(toArray(address())), format(toArray(netmask()))); - } - - public String[] getAllAddresses() { - int ct = getAddressCount(); - String[] addresses = new String[ct]; - if (ct == 0) { - return addresses; - } - for (int add = low(), j = 0; add <= high(); ++add, ++j) { - addresses[j] = format(toArray(add)); - } - return addresses; - } - - /** - * {@inheritDoc} - * - * @since 2.2 - */ - @Override public String toString() { - final StringBuilder buf = new StringBuilder(); - buf.append("CIDR Signature:\t[") - .append(getCidrSignature()) - .append("]") - .append(" Netmask: [") - .append(getNetmask()) - .append("]\n") - .append("Network:\t[") - .append(getNetworkAddress()) - .append("]\n") - .append("Broadcast:\t[") - .append(getBroadcastAddress()) - .append("]\n") - .append("First Address:\t[") - .append(getLowAddress()) - .append("]\n") - .append("Last Address:\t[") - .append(getHighAddress()) - .append("]\n") - .append("# Addresses:\t[") - .append(getAddressCount()) - .append("]\n"); - return buf.toString(); - } - } - - /** - * Return a {@link SubnetInfo} instance that contains subnet-specific statistics - * - * @return new instance - */ - public final SubnetInfo getInfo() { - return new SubnetInfo(); - } - - /* - * Initialize the internal fields from the supplied CIDR mask - */ - private void calculate(String mask) { - Matcher matcher = cidrPattern.matcher(mask); - - if (matcher.matches()) { - address = matchAddress(matcher); - - /* Create a binary netmask from the number of bits specification /x */ - int cidrPart = rangeCheck(Integer.parseInt(matcher.group(5)), 0, NBITS); - for (int j = 0; j < cidrPart; ++j) { - netmask |= (1 << 31 - j); - } - - /* Calculate base network address */ - network = (address & netmask); - - /* Calculate broadcast address */ - broadcast = network | ~(netmask); - } else { - throw new IllegalArgumentException("Could not parse [" + mask + "]"); - } - } - - /* - * Convert a dotted decimal format address to a packed integer format - */ - private int toInteger(String address) { - Matcher matcher = addressPattern.matcher(address); - if (matcher.matches()) { - return matchAddress(matcher); - } else { - throw new IllegalArgumentException("Could not parse [" + address + "]"); - } - } - - /* - * Convenience method to extract the components of a dotted decimal address and - * pack into an integer using a regex match - */ - private int matchAddress(Matcher matcher) { - int addr = 0; - for (int i = 1; i <= 4; ++i) { - int n = (rangeCheck(Integer.parseInt(matcher.group(i)), 0, 255)); - addr |= ((n & 0xff) << 8 * (4 - i)); - } - return addr; - } - - /* - * Convert a packed integer address into a 4-element array - */ - private int[] toArray(int val) { - int ret[] = new int[4]; - for (int j = 3; j >= 0; --j) { - ret[j] |= ((val >>> 8 * (3 - j)) & (0xff)); - } - return ret; - } - - /* - * Convert a 4-element array into dotted decimal format - */ - private String format(int[] octets) { - StringBuilder str = new StringBuilder(); - for (int i = 0; i < octets.length; ++i) { - str.append(octets[i]); - if (i != octets.length - 1) { - str.append("."); - } - } - return str.toString(); - } - - /* - * Convenience function to check integer boundaries. - * Checks if a value x is in the range [begin,end]. - * Returns x if it is in range, throws an exception otherwise. - */ - private int rangeCheck(int value, int begin, int end) { - if (value >= begin && value <= end) { // (begin,end] - return value; - } - - throw new IllegalArgumentException( - "Value [" + value + "] not in range [" + begin + "," + end + "]"); - } - - /* - * Count the number of 1-bits in a 32-bit integer using a divide-and-conquer strategy - * see Hacker's Delight section 5.1 - */ - int pop(int x) { - x = x - ((x >>> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >>> 2) & 0x33333333); - x = (x + (x >>> 4)) & 0x0F0F0F0F; - x = x + (x >>> 8); - x = x + (x >>> 16); - return x & 0x0000003F; - } - - /* Convert two dotted decimal addresses to a single xxx.xxx.xxx.xxx/yy format - * by counting the 1-bit population in the mask address. (It may be better to count - * NBITS-#trailing zeroes for this case) - */ - private String toCidrNotation(String addr, String mask) { - return addr + "/" + pop(toInteger(mask)); - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java deleted file mode 100644 index a609f284..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/TrustManagerUtils.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package aria.apache.commons.net.util; - -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * TrustManager utilities for generating TrustManagers. - * - * @since 3.0 - */ -public final class TrustManagerUtils { - private static final X509Certificate[] EMPTY_X509CERTIFICATE_ARRAY = new X509Certificate[] {}; - - private static class TrustManager implements X509TrustManager { - - private final boolean checkServerValidity; - - TrustManager(boolean checkServerValidity) { - this.checkServerValidity = checkServerValidity; - } - - /** - * Never generates a CertificateException. - */ - @Override public void checkClientTrusted(X509Certificate[] certificates, String authType) { - return; - } - - @Override public void checkServerTrusted(X509Certificate[] certificates, String authType) - throws CertificateException { - if (checkServerValidity) { - for (X509Certificate certificate : certificates) { - certificate.checkValidity(); - } - } - } - - /** - * @return an empty array of certificates - */ - @Override public X509Certificate[] getAcceptedIssuers() { - return EMPTY_X509CERTIFICATE_ARRAY; - } - } - - private static final X509TrustManager ACCEPT_ALL = new TrustManager(false); - - private static final X509TrustManager CHECK_SERVER_VALIDITY = new TrustManager(true); - - /** - * Generate a TrustManager that performs no checks. - * - * @return the TrustManager - */ - public static X509TrustManager getAcceptAllTrustManager() { - return ACCEPT_ALL; - } - - /** - * Generate a TrustManager that checks server certificates for validity, - * but otherwise performs no checks. - * - * @return the validating TrustManager - */ - public static X509TrustManager getValidateServerCertificateTrustManager() { - return CHECK_SERVER_VALIDITY; - } - - /** - * Return the default TrustManager provided by the JVM. - *

- * This should be the same as the default used by - * {@link javax.net.ssl.SSLContext#init(KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)} - * SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} - * when the TrustManager parameter is set to {@code null} - * - * @param keyStore the KeyStore to use, may be {@code null} - * @return the default TrustManager - * @throws GeneralSecurityException if an error occurs - */ - public static X509TrustManager getDefaultTrustManager(KeyStore keyStore) - throws GeneralSecurityException { - String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); - TrustManagerFactory instance = TrustManagerFactory.getInstance(defaultAlgorithm); - instance.init(keyStore); - return (X509TrustManager) instance.getTrustManagers()[0]; - } -} diff --git a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/package-info.java b/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/package-info.java deleted file mode 100644 index 7b95d75d..00000000 --- a/AriaFtpPlug/src/main/java/aria/apache/commons/net/util/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Utility classes - */ -package aria.apache.commons.net.util; \ No newline at end of file diff --git a/DEV_LOG.md b/DEV_LOG.md index 17a10a44..e5633197 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -2,6 +2,7 @@ + v_3.8.3 - fix bug https://github.com/AriaLyy/Aria/issues/573 - android P适配 https://github.com/AriaLyy/Aria/issues/581 + - 添加ftp服务器标志 https://github.com/AriaLyy/Aria/issues/580 + v_3.8.1 (2019/12/22) - 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570 - 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java index 3fa81ac6..05cbe416 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoThread.java @@ -91,7 +91,7 @@ public abstract class AbsFtpInfoThread 1) { if (record.isBlock) { helper.handleBlockRecord(); } else { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index dfe8f2d7..9dcdce50 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -73,22 +73,7 @@ public class RecordHandler implements IRecordHandler { mAdapter.onPre(); mTaskRecord = DbDataHelper.getTaskRecord(getFilePath(), mEntity.getTaskType()); if (mTaskRecord == null) { - if (!new File(getFilePath()).exists()){ - FileUtil.createFile(getFilePath()); - } initRecord(true); - } else { - File file = new File(mTaskRecord.filePath); - if (!file.exists()) { - ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); - DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); - mTaskRecord.threadRecords.clear(); - mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); - initRecord(false); - } else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { - mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); - initRecord(false); - } } mAdapter.handlerTaskRecord(mTaskRecord); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 2975dc29..afe57898 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -149,28 +149,29 @@ public class RecordHelper { * 处理单线程的任务的记录 */ public void handleSingleThreadRecord() { - File file = new File(mTaskRecord.filePath); + // mTaskRecord.isBlock是为了兼容以前的文件格式 + File file = new File( + mTaskRecord.isBlock ? String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0) + : mTaskRecord.filePath); ThreadRecord tr = mTaskRecord.threadRecords.get(0); if (!file.exists()) { ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); tr.startLocation = 0; tr.isComplete = false; tr.endLocation = mWrapper.getEntity().getFileSize(); - } else if (mTaskRecord.isBlock) { - if (file.length() > mWrapper.getEntity().getFileSize()) { - ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); - FileUtil.deleteFile(file); - tr.startLocation = 0; + } else if (file.length() > mWrapper.getEntity().getFileSize()) { + ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); + FileUtil.deleteFile(file); + tr.startLocation = 0; + tr.isComplete = false; + tr.endLocation = mWrapper.getEntity().getFileSize(); + } else if (file.length() == mWrapper.getEntity().getFileSize()) { + tr.isComplete = true; + } else { + if (file.length() != tr.startLocation) { + ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length())); + tr.startLocation = file.length(); tr.isComplete = false; - tr.endLocation = mWrapper.getEntity().getFileSize(); - } else if (file.length() == mWrapper.getEntity().getFileSize()) { - tr.isComplete = true; - } else { - if (file.length() != tr.startLocation) { - ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length())); - tr.startLocation = file.length(); - tr.isComplete = false; - } } } mWrapper.setNewTask(false); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java index c3fd33d8..c51ee550 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java @@ -202,6 +202,11 @@ public class ThreadStateManager implements IThreadState { * @return {@code true} 合并成功,{@code false}合并失败 */ private boolean mergeFile() { + if (mTaskRecord.threadNum == 1) { + File partFile = new File(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, 0)); + return partFile.renameTo(new File(mTaskRecord.filePath)); + } + List partPath = new ArrayList<>(); for (int i = 0, len = mTaskRecord.threadNum; i < len; i++) { partPath.add(String.format(IRecordHandler.SUB_PATH, mTaskRecord.filePath, i)); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java index 641bd1f1..e7e3ea28 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DBConfig.java @@ -34,7 +34,7 @@ class DBConfig { static boolean DEBUG = false; static Map> mapping = new LinkedHashMap<>(); static String DB_NAME; - static int VERSION = 57; + static int VERSION = 58; /** * 是否将数据库保存在Sd卡,{@code true} 是 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java index 6cfd18cd..ed785f89 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -28,6 +28,7 @@ import com.arialyy.aria.util.ALog; import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -163,7 +164,49 @@ final class SqlHelper extends SQLiteOpenHelper { for (String tableName : tables) { Class clazz = DBConfig.mapping.get(tableName); if (SqlUtil.tableExists(db, clazz)) { - //修改表名为中介表名 + // ----------- 1、获取旧表字段、新表字段 + Cursor columnC = + db.rawQuery(String.format("PRAGMA table_info(%s)", tableName), null); + + // 获取新表的所有字段名称 + List newTabColumns = SqlUtil.getColumns(clazz); + // 获取旧表的所有字段名称 + List oldTabColumns = new ArrayList<>(); + + while (columnC.moveToNext()) { + String columnName = columnC.getString(columnC.getColumnIndex("name")); + oldTabColumns.add(columnName); + } + columnC.close(); + + // ----------- 2、为防止字段增加失败的情况,先给旧表增加字段 + List newAddColum = getNewColumn(newTabColumns, oldTabColumns); + // 删除重命名的字段 + Map modifyMap = null; + if (modifyColumns != null){ + modifyMap = modifyColumns.get(tableName); + if (modifyMap != null){ + Iterator it = newAddColum.iterator(); + while (it.hasNext()){ + String s = it.next(); + if (modifyMap.get(s) != null){ + it.remove(); + } + } + } + } + + // 给旧表增加字段,防止新增字段失败 + if (newAddColum.size() > 0){ + String sql = "ALTER TABLE %s ADD COLUMN %s %s"; + for (String nc : newAddColum){ + String temp = String.format(sql, tableName, nc, SqlUtil.getColumnTypeByFieldName(clazz, nc)); + ALog.d(TAG, "添加表字段的sql:" + temp); + db.execSQL(temp); + } + } + + // ----------- 3、将旧表备份下,并创建新表 String alertSql = String.format("ALTER TABLE %s RENAME TO %s_temp", tableName, tableName); db.execSQL(alertSql); @@ -176,28 +219,15 @@ final class SqlHelper extends SQLiteOpenHelper { long count = cursor.getLong(0); cursor.close(); - // 复制数据 + // ----------- 4、将旧表数据复制到新表 if (count > 0) { - Cursor columnC = - db.rawQuery(String.format("PRAGMA table_info(%s_temp)", tableName), null); - - // 获取新表的所有字段名称 - List newTabColumns = SqlUtil.getColumns(clazz); - // 获取旧表的所有字段名称 - List oldTabColumns = new ArrayList<>(); - - while (columnC.moveToNext()) { - String columnName = columnC.getString(columnC.getColumnIndex("name")); - oldTabColumns.add(columnName); - } - columnC.close(); // 旧表需要删除的字段,删除旧表有而新表没的字段 List diffTab = getDiffColumn(newTabColumns, oldTabColumns); StringBuilder params = new StringBuilder(); // 需要修改的列名映射表 - Map modifyMap = null; + //Map modifyMap = null; if (modifyColumns != null) { modifyMap = modifyColumns.get(tableName); } @@ -227,11 +257,11 @@ final class SqlHelper extends SQLiteOpenHelper { String insertSql = String.format("INSERT INTO %s (%s) SELECT %s FROM %s_temp", tableName, newParamStr, oldParamStr, tableName); - ALog.d(TAG, "insertSql = " + insertSql); + ALog.d(TAG, "恢复数据的sql:" + insertSql); db.execSQL(insertSql); } - //删除中介表 + // ----------- 5、删除备份的表 SqlUtil.dropTable(db, tableName + "_temp"); } else { SqlUtil.createTable(db, clazz); @@ -258,6 +288,19 @@ final class SqlHelper extends SQLiteOpenHelper { return temp; } + /** + * 获取新增字段 + * + * @param newTab 新表字段 + * @param oldTab 就表字段 + * @return 新表有而旧表没的字段 + */ + private List getNewColumn(List newTab, List oldTab) { + List temp = new ArrayList<>(newTab); + temp.removeAll(oldTab); + return temp; + } + /** * 给TaskRecord 增加任务类型 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java index e3141c6d..ff173eb2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/SqlUtil.java @@ -240,28 +240,13 @@ final class SqlUtil { continue; } Class type = field.getType(); - sb.append(field.getName()); - if (type == String.class || type.isEnum()) { - sb.append(" VARCHAR"); - } else if (type == int.class || type == Integer.class) { - sb.append(" INTEGER"); - } else if (type == float.class || type == Float.class) { - sb.append(" FLOAT"); - } else if (type == double.class || type == Double.class) { - sb.append(" DOUBLE"); - } else if (type == long.class || type == Long.class) { - sb.append(" BIGINT"); - } else if (type == boolean.class || type == Boolean.class) { - sb.append(" BOOLEAN"); - } else if (type == java.util.Date.class || type == java.sql.Date.class) { - sb.append(" DATA"); - } else if (type == byte.class || type == Byte.class) { - sb.append(" BLOB"); - } else if (type == Map.class || type == List.class) { - sb.append(" TEXT"); - } else { + String columnType = getColumnType(type); + if (columnType == null) { continue; } + sb.append(field.getName()); + sb.append(" ").append(columnType); + if (SqlUtil.isPrimary(field)) { Primary pk = field.getAnnotation(Primary.class); sb.append(" PRIMARY KEY"); @@ -315,10 +300,51 @@ final class SqlUtil { String str = sb.toString(); str = str.substring(0, str.length() - 1) + ");"; + ALog.d(TAG, "创建表的sql:" + str); db.execSQL(str); } } + /** + * 根据字段名获取字段类型 + */ + static String getColumnTypeByFieldName(Class tabClass, String fieldName) { + List fields = CommonUtil.getAllFields(tabClass); + for (Field field : fields) { + if (field.getName().equals(fieldName)) { + return getColumnType(field.getType()); + } + } + return null; + } + + /** + * 获取字段类型 + */ + static String getColumnType(Class fieldtype) { + if (fieldtype == String.class || fieldtype.isEnum()) { + return "VARCHAR"; + } else if (fieldtype == int.class || fieldtype == Integer.class) { + return "INTEGER"; + } else if (fieldtype == float.class || fieldtype == Float.class) { + return "FLOAT"; + } else if (fieldtype == double.class || fieldtype == Double.class) { + return "DOUBLE"; + } else if (fieldtype == long.class || fieldtype == Long.class) { + return "BIGINT"; + } else if (fieldtype == boolean.class || fieldtype == Boolean.class) { + return "BOOLEAN"; + } else if (fieldtype == java.util.Date.class || fieldtype == java.sql.Date.class) { + return "DATA"; + } else if (fieldtype == byte.class || fieldtype == Byte.class) { + return "BLOB"; + } else if (fieldtype == Map.class || fieldtype == List.class) { + return "TEXT"; + } else { + return null; + } + } + /** * URL编码字符串 * diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 8efca66d..6f8e28fa 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -48,7 +48,7 @@ public class FtpDownloadActivity extends BaseActivity getFtpDownloadInfo(Context context) { //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); - String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; + //String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; + String url = "ftp://58.210.178.52:21/battery1.0.0.apk"; String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath); singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 801b91f0..ba3e6cd8 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -272,9 +272,9 @@ public class SingleTaskActivity extends BaseActivity { .load(mTaskId) .stop(); } else { - mTaskId = Aria.download(this).load(mTaskId) + Aria.download(this).load(mTaskId) //.updateUrl(mUrl) - .reStart(); + .resume(); } break; case R.id.cancel: diff --git a/build.gradle b/build.gradle index 84852cd5..92fb3cba 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ task clean(type: Delete) { ext { versionCode = 383 - versionName = '3.8.3_beta' + versionName = '3.8.3_beta_1' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName diff --git a/settings.gradle b/settings.gradle index ac3cec1e..cee643c8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AriaFtpPlug', ':AppFrame', ':HttpComponent', ':M3U8Component', ':SFtpComponent', +include ':app', ':Aria', ':AriaAnnotations', ':AriaCompiler', ':AppFrame', ':HttpComponent', ':M3U8Component', ':SFtpComponent', ':FtpComponent', ':PublicComponent' From 1e237d795d3a2563fdcf5956b652327c443a49fc Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Sat, 28 Dec 2019 13:18:20 +0800 Subject: [PATCH 045/140] =?UTF-8?q?loader=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/arialyy/aria/core/AriaManager.java | 2 +- .../core/manager/DTaskWrapperFactory.java | 2 +- .../aria/apache/commons/net/ftp/FTPFile.java | 2 +- .../net/ftp/parser/NetwareFTPEntryParser.java | 2 +- .../net/ftp/parser/VMSFTPEntryParser.java | 2 +- ...cordAdapter.java => FtpRecordHandler.java} | 8 +- .../aria/ftp/download/FtpDLoaderAdapter.java | 6 +- .../aria/ftp/download/FtpDirDLoaderUtil.java | 4 +- .../aria/ftp/upload/FtpULoaferAdapter.java | 6 +- ...eInfoThread.java => HttpFileInfoTask.java} | 30 ++- .../arialyy/aria/http/HttpGroupInfoTask.java | 167 +++++++++++++++ ...ordAdapter.java => HttpRecordHandler.java} | 14 +- .../aria/http/download/DGroupLoaderUtil.java | 8 +- .../http/download/HttpDLoaderAdapter.java | 200 +++++++++--------- .../aria/http/download/HttpDLoaderUtil.java | 36 ++-- .../aria/http/download/HttpDTTBuilder.java | 62 ++++++ .../http/download/HttpSubDLoaderUtil.java | 4 +- .../aria/http/upload/HttpULoaderAdapter.java | 6 +- .../com/arialyy/aria/m3u8/BaseM3U8Loader.java | 4 +- ...ordAdapter.java => M3U8RecordHandler.java} | 8 +- .../core/common/AbsRecordHandlerAdapter.java | 44 ---- .../aria/core/common/RecordHandler.java | 56 +++-- .../aria/core/common/RecordHelper.java | 2 +- .../arialyy/aria/core/config/XMLReader.java | 4 +- ...GroupUtil.java => AbsGroupLoaderUtil.java} | 4 +- .../aria/core/group/SimpleSchedulers.java | 2 +- .../aria/core/group/SimpleSubQueue.java | 2 +- .../arialyy/aria/core/inf/IThreadState.java | 15 +- .../aria/core/listener/BaseDListener.java | 2 +- .../aria/core/listener/BaseUListener.java | 2 +- .../core/listener/DownloadGroupListener.java | 2 +- .../arialyy/aria/core/loader/AbsLoader.java | 97 +++++---- .../aria/core/loader/AbsNormalLoaderUtil.java | 36 ++-- .../aria/core/loader/AbsNormalTTBuilder.java | 185 ++++++++++++++++ .../IInfoTask.java} | 40 ++-- .../com/arialyy/aria/core/loader/ILoader.java | 15 +- .../aria/core/loader/ILoaderAdapter.java | 1 - ...erIntercept.java => ILoaderComponent.java} | 32 +-- ...erInterceptor.java => ILoaderVisitor.java} | 43 ++-- .../IRecordHandler.java} | 37 +++- .../IThreadTaskBuilder.java} | 31 +-- .../arialyy/aria/core/loader/LoaderChain.java | 75 ------- ...dInterceptor.java => LoaderStructure.java} | 38 ++-- .../aria/core/loader/NormalLoader.java | 186 +++++----------- .../aria/core/loader/ThreadStateManager.java | 140 ++++++------ .../arialyy/aria/core/task/AbsGroupTask.java | 6 +- .../arialyy/aria/core/task/IThreadTask.java | 6 + .../arialyy/aria/core/task/ThreadTask.java | 4 + .../java/com/arialyy/aria/util/CheckUtil.java | 2 +- .../com/arialyy/aria/util/RecordUtil.java | 2 +- app/gradle.properties | 4 +- build.gradle | 4 +- 52 files changed, 1003 insertions(+), 689 deletions(-) rename FtpComponent/src/main/java/com/arialyy/aria/ftp/{FtpRecordAdapter.java => FtpRecordHandler.java} (93%) rename HttpComponent/src/main/java/com/arialyy/aria/http/{HttpFileInfoThread.java => HttpFileInfoTask.java} (94%) create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/HttpGroupInfoTask.java rename HttpComponent/src/main/java/com/arialyy/aria/http/{HttpRecordAdapter.java => HttpRecordHandler.java} (90%) create mode 100644 HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDTTBuilder.java rename M3U8Component/src/main/java/com/arialyy/aria/m3u8/{M3U8RecordAdapter.java => M3U8RecordHandler.java} (95%) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java rename PublicComponent/src/main/java/com/arialyy/aria/core/group/{AbsGroupUtil.java => AbsGroupLoaderUtil.java} (98%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java rename PublicComponent/src/main/java/com/arialyy/aria/core/{inf/OnFileInfoCallback.java => loader/IInfoTask.java} (60%) rename PublicComponent/src/main/java/com/arialyy/aria/core/loader/{LoaderIntercept.java => ILoaderComponent.java} (63%) rename PublicComponent/src/main/java/com/arialyy/aria/core/loader/{ILoaderInterceptor.java => ILoaderVisitor.java} (60%) rename PublicComponent/src/main/java/com/arialyy/aria/core/{inf/IRecordHandlerAdapter.java => loader/IRecordHandler.java} (69%) rename PublicComponent/src/main/java/com/arialyy/aria/core/{inf/IRecordHandler.java => loader/IThreadTaskBuilder.java} (55%) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderChain.java rename PublicComponent/src/main/java/com/arialyy/aria/core/loader/{RecordInterceptor.java => LoaderStructure.java} (50%) diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index a562e53b..c7483c7c 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -35,7 +35,7 @@ import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.IReceiver; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.inf.ReceiverType; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadReceiver; diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java index 16e6592d..6f08c620 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTaskWrapperFactory.java @@ -19,7 +19,7 @@ import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.io.File; diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java index 5edcc119..3a9dd625 100644 --- a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/FTPFile.java @@ -55,7 +55,7 @@ public class FTPFile implements Serializable { /** A constant indicating file/directory write permission. ***/ public static final int WRITE_PERMISSION = 1; /** - * A constant indicating file execute permission or directory listing + * A constant indicating file accept permission or directory listing * permission. ***/ public static final int EXECUTE_PERMISSION = 2; diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java index 7585ebbe..212198b3 100644 --- a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java @@ -95,7 +95,7 @@ public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { * Netware file permissions are in the following format: RWCEAFMS, and are explained as follows: *

    *
  • S - Supervisor; All rights. - *
  • R - Read; Right to open and read or execute. + *
  • R - Read; Right to open and read or accept. *
  • W - Write; Right to open and modify. *
  • C - Create; Right to create; when assigned to a file, allows a deleted file to be * recovered. diff --git a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java index b2578cd0..6c23179c 100644 --- a/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java +++ b/FtpComponent/src/main/java/aria/apache/commons/net/ftp/parser/VMSFTPEntryParser.java @@ -164,7 +164,7 @@ public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl { //Set file permission. //VMS has (SYSTEM,OWNER,GROUP,WORLD) users that can contain - //R (read) W (write) E (execute) D (delete) + //R (read) W (write) E (accept) D (delete) //iterate for OWNER GROUP WORLD permissions for (int access = 0; access < 3; access++) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordHandler.java similarity index 93% rename from FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java rename to FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordHandler.java index 8a3bd02a..79a3e0ea 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpRecordHandler.java @@ -17,11 +17,11 @@ package com.arialyy.aria.ftp; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; -import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; +import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.common.RecordHelper; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.RecordUtil; @@ -31,9 +31,9 @@ import java.util.ArrayList; * @Author lyy * @Date 2019-09-19 */ -public class FtpRecordAdapter extends AbsRecordHandlerAdapter { +public class FtpRecordHandler extends RecordHandler { - public FtpRecordAdapter(AbsTaskWrapper wrapper) { + public FtpRecordHandler(AbsTaskWrapper wrapper) { super(wrapper); } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java index b063972e..6e81ef70 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java @@ -18,13 +18,13 @@ package com.arialyy.aria.ftp.download; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.ftp.FtpRecordAdapter; +import com.arialyy.aria.ftp.FtpRecordHandler; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; import java.io.File; @@ -66,7 +66,7 @@ public class FtpDLoaderAdapter extends AbsNormalLoaderAdapter { } @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { - FtpRecordAdapter adapter = new FtpRecordAdapter(wrapper); + FtpRecordHandler adapter = new FtpRecordHandler(wrapper); RecordHandler handler = new RecordHandler(wrapper); handler.setAdapter(adapter); return handler; diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java index 9344f982..e58a665b 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java @@ -21,7 +21,7 @@ import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.group.AbsGroupUtil; +import com.arialyy.aria.core.group.AbsGroupLoaderUtil; import com.arialyy.aria.core.group.AbsSubDLoadUtil; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.OnFileInfoCallback; @@ -37,7 +37,7 @@ import java.util.concurrent.locks.ReentrantLock; * Created by Aria.Lao on 2017/7/27. * ftp文件夹下载工具 */ -public class FtpDirDLoaderUtil extends AbsGroupUtil { +public class FtpDirDLoaderUtil extends AbsGroupLoaderUtil { private ReentrantLock LOCK = new ReentrantLock(); private Condition condition = LOCK.newCondition(); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java index f3a44b2b..3ec3312f 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaferAdapter.java @@ -21,10 +21,10 @@ import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.common.SubThreadConfig; import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.ftp.FtpRecordAdapter; +import com.arialyy.aria.ftp.FtpRecordHandler; /** * @Author lyy @@ -48,7 +48,7 @@ class FtpULoaferAdapter extends AbsNormalLoaderAdapter { } @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { - FtpRecordAdapter adapter = new FtpRecordAdapter(wrapper); + FtpRecordHandler adapter = new FtpRecordHandler(wrapper); RecordHandler handler = new RecordHandler(wrapper); handler.setAdapter(adapter); return handler; diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java similarity index 94% rename from HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java index e484c5cc..7d877bc7 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoThread.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java @@ -24,7 +24,8 @@ import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.RequestEnum; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.OnFileInfoCallback; +import com.arialyy.aria.core.loader.IInfoTask; +import com.arialyy.aria.core.loader.ILoaderVisitor; import com.arialyy.aria.core.processor.IHttpFileLenAdapter; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; @@ -54,19 +55,18 @@ import java.util.UUID; /** * 下载文件信息获取 */ -public class HttpFileInfoThread implements Runnable { +public class HttpFileInfoTask implements IInfoTask, Runnable { private static final String TAG = "HttpFileInfoThread"; private DownloadEntity mEntity; private DTaskWrapper mTaskWrapper; private int mConnectTimeOut; - private OnFileInfoCallback onFileInfoCallback; + private Callback callback; private HttpTaskOption taskOption; - public HttpFileInfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { + public HttpFileInfoTask(DTaskWrapper taskWrapper) { this.mTaskWrapper = taskWrapper; mEntity = taskWrapper.getEntity(); mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut(); - onFileInfoCallback = callback; taskOption = (HttpTaskOption) taskWrapper.getTaskOption(); } @@ -99,6 +99,10 @@ public class HttpFileInfoThread implements Runnable { } } + @Override public void setCallback(Callback callback) { + this.callback = callback; + } + private void handleConnect(HttpURLConnection conn) throws IOException { if (taskOption.getRequestEnum() == RequestEnum.POST) { Map params = taskOption.getParams(); @@ -223,9 +227,9 @@ public class HttpFileInfoThread implements Runnable { } if (end) { taskOption.setChunked(isChunked); - if (onFileInfoCallback != null) { + if (callback != null) { CompleteInfo info = new CompleteInfo(code, mTaskWrapper); - onFileInfoCallback.onComplete(mEntity.getUrl(), info); + callback.onSucceed(mEntity.getUrl(), info); } mEntity.update(); } @@ -291,8 +295,8 @@ public class HttpFileInfoThread implements Runnable { private void handleUrlReTurn(HttpURLConnection conn, String newUrl) throws IOException { ALog.d(TAG, "30x跳转,新url为【" + newUrl + "】"); if (TextUtils.isEmpty(newUrl) || newUrl.equalsIgnoreCase("null")) { - if (onFileInfoCallback != null) { - onFileInfoCallback.onFail(mEntity, new TaskException(TAG, "获取重定向链接失败"), false); + if (callback != null) { + callback.onFail(mEntity, new TaskException(TAG, "获取重定向链接失败"), false); } return; } @@ -336,11 +340,15 @@ public class HttpFileInfoThread implements Runnable { } private void failDownload(BaseException e, boolean needRetry) { - if (onFileInfoCallback != null) { - onFileInfoCallback.onFail(mEntity, e, needRetry); + if (callback != null) { + callback.onFail(mEntity, e, needRetry); } } + @Override public void accept(ILoaderVisitor visitor) { + visitor.addComponent(this); + } + private static class FileLenAdapter implements IHttpFileLenAdapter { @Override public long handleFileLen(Map> headers) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpGroupInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpGroupInfoTask.java new file mode 100644 index 00000000..e7ecacf5 --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpGroupInfoTask.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.http; + +import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; +import com.arialyy.aria.core.download.DGTaskWrapper; +import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.listener.DownloadGroupListener; +import com.arialyy.aria.core.loader.IInfoTask; +import com.arialyy.aria.core.loader.ILoaderVisitor; +import com.arialyy.aria.exception.AriaIOException; +import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 组合任务文件信息,用于获取长度未知时,组合任务的长度 + */ +public class HttpGroupInfoTask implements IInfoTask { + private String TAG = CommonUtil.getClassName(this); + private Callback callback; + private DGTaskWrapper wrapper; + private final Object LOCK = new Object(); + private ExecutorService mPool = null; + private boolean getLenComplete = false; + private int count; + private int failCount; + private DownloadGroupListener listener; + + /** + * 子任务回调 + */ + private Callback subCallback = new Callback() { + @Override public void onSucceed(String url, CompleteInfo info) { + count++; + checkGetSizeComplete(count, failCount); + ALog.d(TAG, "获取子任务信息完成"); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + ALog.e(TAG, String.format("获取文件信息失败,url:%s", ((DownloadEntity) entity).getUrl())); + count++; + failCount++; + listener.onSubFail((DownloadEntity) entity, new AriaIOException(TAG, + String.format("子任务获取文件长度失败,url:%s", ((DownloadEntity) entity).getUrl()))); + checkGetSizeComplete(count, failCount); + } + }; + + public HttpGroupInfoTask(DGTaskWrapper wrapper, DownloadGroupListener listener) { + this.wrapper = wrapper; + this.listener = listener; + } + + @Override public void run() { + // 如果是isUnknownSize()标志,并且获取大小没有完成,则直接回调onStop + if (mPool != null && !getLenComplete) { + ALog.d(TAG, "获取长度未完成的情况下,停止组合任务"); + mPool.shutdown(); + //mListener.onStop(0); + return; + } + // 处理组合任务大小未知的情况 + if (wrapper.isUnknownSize() && wrapper.getEntity().getFileSize() < 1) { + mPool = Executors.newCachedThreadPool(); + getGroupSize(); + try { + synchronized (LOCK) { + LOCK.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) { + cloneHeader(wrapper); + } + callback.onSucceed(wrapper.getKey(), new CompleteInfo()); + } + } + + /** + * 获取组合任务大小,使用该方式获取到的组合任务大小,子任务不需要再重新获取文件大小 + */ + private void getGroupSize() { + new Thread(new Runnable() { + @Override public void run() { + for (DTaskWrapper dTaskWrapper : wrapper.getSubTaskWrapper()) { + cloneHeader(dTaskWrapper); + HttpFileInfoTask infoTask = new HttpFileInfoTask(dTaskWrapper); + infoTask.setCallback(subCallback); + } + } + }).start(); + } + + /** + * 检查组合任务大小是否获取完成,获取完成后取消阻塞,并设置组合任务大小 + */ + private void checkGetSizeComplete(int count, int failCount) { + if (failCount == wrapper.getSubTaskWrapper().size()) { + callback.onFail(wrapper.getEntity(), new AriaIOException(TAG, "获取子任务长度失败"), false); + notifyLock(); + return; + } + if (count == wrapper.getSubTaskWrapper().size()) { + long size = 0; + for (DTaskWrapper wrapper : wrapper.getSubTaskWrapper()) { + size += wrapper.getEntity().getFileSize(); + } + wrapper.getEntity().setConvertFileSize(CommonUtil.formatFileSize(size)); + wrapper.getEntity().setFileSize(size); + wrapper.getEntity().update(); + getLenComplete = true; + ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s,失败的只任务数:%s", size, failCount)); + callback.onSucceed(wrapper.getKey(), new CompleteInfo()); + notifyLock(); + } + } + + private void notifyLock() { + synchronized (LOCK) { + LOCK.notifyAll(); + } + } + + /** + * 子任务使用父包裹器的属性 + */ + private void cloneHeader(DTaskWrapper taskWrapper) { + HttpTaskOption groupOption = (HttpTaskOption) wrapper.getTaskOption(); + HttpTaskOption subOption = new HttpTaskOption(); + + // 设置属性 + subOption.setFileLenAdapter(groupOption.getFileLenAdapter()); + subOption.setRequestEnum(groupOption.getRequestEnum()); + subOption.setHeaders(groupOption.getHeaders()); + subOption.setProxy(groupOption.getProxy()); + subOption.setParams(groupOption.getParams()); + taskWrapper.setTaskOption(subOption); + } + + @Override public void setCallback(Callback callback) { + this.callback = callback; + } + + @Override public void accept(ILoaderVisitor visitor) { + visitor.addComponent(this); + } +} diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordHandler.java similarity index 90% rename from HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java rename to HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordHandler.java index 1dbba97b..ecdbbe0f 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpRecordHandler.java @@ -17,11 +17,11 @@ package com.arialyy.aria.http; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; -import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; +import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.common.RecordHelper; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.RecordUtil; @@ -31,16 +31,16 @@ import java.util.ArrayList; * @Author lyy * @Date 2019-09-23 */ -public class HttpRecordAdapter extends AbsRecordHandlerAdapter { - public HttpRecordAdapter(AbsTaskWrapper wrapper) { +public class HttpRecordHandler extends RecordHandler { + public HttpRecordHandler(AbsTaskWrapper wrapper) { super(wrapper); } @Override public void onPre() { super.onPre(); - if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP) { - RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD); - } + //if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP) { + // RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD); + //} } @Override public void handlerTaskRecord(TaskRecord record) { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java index 016576c7..f2e457e4 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java @@ -25,9 +25,9 @@ import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.AriaIOException; import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.core.group.AbsGroupUtil; +import com.arialyy.aria.core.group.AbsGroupLoaderUtil; import com.arialyy.aria.core.group.AbsSubDLoadUtil; -import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpFileInfoTask; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -40,7 +40,7 @@ import java.util.concurrent.Executors; * Created by AriaL on 2017/6/30. * 任务组下载工具 */ -public class DGroupLoaderUtil extends AbsGroupUtil { +public class DGroupLoaderUtil extends AbsGroupLoaderUtil { private final Object LOCK = new Object(); private ExecutorService mPool = null; private boolean getLenComplete = false; @@ -113,7 +113,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil { @Override public void run() { for (DTaskWrapper dTaskWrapper : getWrapper().getSubTaskWrapper()) { cloneHeader(dTaskWrapper); - mPool.submit(new HttpFileInfoThread(dTaskWrapper, new OnFileInfoCallback() { + mPool.submit(new HttpFileInfoTask(dTaskWrapper, new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { if (!getWrapper().isUnknownSize()) { startSubLoader(createSubLoader((DTaskWrapper) info.wrapper, false)); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java index 0b5ed415..2510958a 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderAdapter.java @@ -1,100 +1,100 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.http.download; - -import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IRecordHandler; -import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; -import com.arialyy.aria.core.task.IThreadTask; -import com.arialyy.aria.core.task.ThreadTask; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.http.HttpRecordAdapter; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.BufferedRandomAccessFile; -import com.arialyy.aria.util.FileUtil; -import java.io.File; -import java.io.IOException; - -/** - * @Author lyy - * @Date 2019-09-21 - */ -final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter { - HttpDLoaderAdapter(ITaskWrapper wrapper) { - super(wrapper); - } - - @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { - if (!record.isBlock) { - if (getTempFile().exists()) { - FileUtil.deleteFile(getTempFile()); - } - } else { - for (int i = 0; i < totalThreadNum; i++) { - File blockFile = - new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); - if (blockFile.exists()) { - ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); - FileUtil.deleteFile(blockFile); - } - } - } - BufferedRandomAccessFile file = null; - try { - if (totalThreadNum > 1 && !record.isBlock) { - file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); - //设置文件长度 - file.setLength(getEntity().getFileSize()); - } - return true; - } catch (IOException e) { - e.printStackTrace(); - ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), - getEntity().getUrl())); - } finally { - if (file != null) { - try { - file.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return false; - } - - @Override public IThreadTask createThreadTask(SubThreadConfig config) { - ThreadTask task = new ThreadTask(config); - HttpDThreadTaskAdapter adapter = new HttpDThreadTaskAdapter(config); - task.setAdapter(adapter); - return task; - } - - @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { - RecordHandler recordHandler = new RecordHandler(wrapper); - HttpRecordAdapter adapter = new HttpRecordAdapter(wrapper); - recordHandler.setAdapter(adapter); - return recordHandler; - } - - private DownloadEntity getEntity() { - return (DownloadEntity) getWrapper().getEntity(); - } -} +///* +// * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package com.arialyy.aria.http.download; +// +//import com.arialyy.aria.core.TaskRecord; +//import com.arialyy.aria.core.common.RecordHandler; +//import com.arialyy.aria.core.common.SubThreadConfig; +//import com.arialyy.aria.core.download.DownloadEntity; +//import com.arialyy.aria.core.loader.IRecordHandler; +//import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; +//import com.arialyy.aria.core.task.IThreadTask; +//import com.arialyy.aria.core.task.ThreadTask; +//import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +//import com.arialyy.aria.core.wrapper.ITaskWrapper; +//import com.arialyy.aria.http.HttpRecordHandler; +//import com.arialyy.aria.util.ALog; +//import com.arialyy.aria.util.BufferedRandomAccessFile; +//import com.arialyy.aria.util.FileUtil; +//import java.io.File; +//import java.io.IOException; +// +///** +// * @Author lyy +// * @Date 2019-09-21 +// */ +//final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter { +// HttpDLoaderAdapter(ITaskWrapper wrapper) { +// super(wrapper); +// } +// +// @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { +// if (!record.isBlock) { +// if (getTempFile().exists()) { +// FileUtil.deleteFile(getTempFile()); +// } +// } else { +// for (int i = 0; i < totalThreadNum; i++) { +// File blockFile = +// new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); +// if (blockFile.exists()) { +// ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); +// FileUtil.deleteFile(blockFile); +// } +// } +// } +// BufferedRandomAccessFile file = null; +// try { +// if (totalThreadNum > 1 && !record.isBlock) { +// file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); +// //设置文件长度 +// file.setLength(getEntity().getFileSize()); +// } +// return true; +// } catch (IOException e) { +// e.printStackTrace(); +// ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), +// getEntity().getUrl())); +// } finally { +// if (file != null) { +// try { +// file.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } +// return false; +// } +// +// @Override public IThreadTask createThreadTask(SubThreadConfig config) { +// ThreadTask task = new ThreadTask(config); +// HttpDThreadTaskAdapter adapter = new HttpDThreadTaskAdapter(config); +// task.setAdapter(adapter); +// return task; +// } +// +// @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { +// RecordHandler recordHandler = new RecordHandler(wrapper); +// HttpRecordHandler adapter = new HttpRecordHandler(wrapper); +// recordHandler.setAdapter(adapter); +// return recordHandler; +// } +// +// private DownloadEntity getEntity() { +// return (DownloadEntity) getWrapper().getEntity(); +// } +//} diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java index d66025e1..846ebedd 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDLoaderUtil.java @@ -15,17 +15,16 @@ */ package com.arialyy.aria.http.download; -import com.arialyy.aria.core.common.AbsEntity; -import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.download.DTaskWrapper; -import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.AbsLoader; import com.arialyy.aria.core.loader.AbsNormalLoaderUtil; +import com.arialyy.aria.core.loader.LoaderStructure; import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.loader.ThreadStateManager; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpFileInfoTask; +import com.arialyy.aria.http.HttpRecordHandler; import com.arialyy.aria.http.HttpTaskOption; /** @@ -38,24 +37,17 @@ public class HttpDLoaderUtil extends AbsNormalLoaderUtil { wrapper.generateTaskOption(HttpTaskOption.class); } - @Override protected AbsLoader createLoader() { - NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper()); - HttpDLoaderAdapter adapter = new HttpDLoaderAdapter(getTaskWrapper()); - loader.setAdapter(adapter); - return loader; + @Override public AbsLoader getLoader() { + return mLoader == null ? new NormalLoader(getTaskWrapper(), getListener()) : mLoader; } - @Override protected Runnable createInfoThread() { - return new HttpFileInfoThread((DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() { - @Override public void onComplete(String url, CompleteInfo info) { - ((NormalLoader) getLoader()).updateTempFile(); - getLoader().start(); - } - - @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - fail(e, needRetry); - getLoader().closeTimer(); - } - }); + public LoaderStructure getLoaderStructure() { + LoaderStructure structure = new LoaderStructure(); + structure.addComponent(new HttpRecordHandler(getTaskWrapper())) + .addComponent(new ThreadStateManager(getListener())) + .addComponent(new HttpFileInfoTask((DTaskWrapper) getTaskWrapper())) + .addComponent(new HttpDTTBuilder(getTaskWrapper())); + structure.accept(getLoader()); + return structure; } } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDTTBuilder.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDTTBuilder.java new file mode 100644 index 00000000..eb22a80c --- /dev/null +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDTTBuilder.java @@ -0,0 +1,62 @@ +package com.arialyy.aria.http.download; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.loader.AbsNormalTTBuilder; +import com.arialyy.aria.core.loader.IRecordHandler; +import com.arialyy.aria.core.task.IThreadTaskAdapter; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.FileUtil; +import java.io.File; +import java.io.IOException; + +final class HttpDTTBuilder extends AbsNormalTTBuilder { + HttpDTTBuilder(AbsTaskWrapper wrapper) { + super(wrapper); + } + + @Override public IThreadTaskAdapter getAdapter(SubThreadConfig config) { + return new HttpDThreadTaskAdapter(config); + } + + @Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) { + if (!record.isBlock) { + if (getTempFile().exists()) { + FileUtil.deleteFile(getTempFile()); + } + } else { + for (int i = 0; i < totalThreadNum; i++) { + File blockFile = + new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i)); + if (blockFile.exists()) { + ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); + FileUtil.deleteFile(blockFile); + } + } + } + BufferedRandomAccessFile file = null; + try { + if (totalThreadNum > 1 && !record.isBlock) { + file = new BufferedRandomAccessFile(new File(getTempFile().getPath()), "rwd", 8192); + //设置文件长度 + file.setLength(getEntity().getFileSize()); + } + return true; + } catch (IOException e) { + e.printStackTrace(); + ALog.e(TAG, String.format("下载失败,filePath: %s, url: %s", getEntity().getFilePath(), + getEntity().getUrl())); + } finally { + if (file != null) { + try { + file.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return false; + } +} diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java index cb19d22e..03c5117e 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java @@ -25,7 +25,7 @@ import com.arialyy.aria.core.inf.OnFileInfoCallback; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.loader.NormalLoader; import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.http.HttpFileInfoThread; +import com.arialyy.aria.http.HttpFileInfoTask; /** * @Author lyy @@ -49,7 +49,7 @@ class HttpSubDLoaderUtil extends AbsSubDLoadUtil { @Override public void start() { if (isNeedGetInfo()) { - new Thread(new HttpFileInfoThread(getWrapper(), new OnFileInfoCallback() { + new Thread(new HttpFileInfoTask(getWrapper(), new OnFileInfoCallback() { @Override public void onComplete(String url, CompleteInfo info) { getDownloader().start(); diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java index e8899840..d22a0d28 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/upload/HttpULoaderAdapter.java @@ -18,13 +18,13 @@ package com.arialyy.aria.http.upload; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.common.SubThreadConfig; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.task.AbsNormalLoaderAdapter; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; -import com.arialyy.aria.http.HttpRecordAdapter; +import com.arialyy.aria.http.HttpRecordHandler; /** * @Author lyy @@ -48,7 +48,7 @@ final class HttpULoaderAdapter extends AbsNormalLoaderAdapter { @Override public IRecordHandler recordHandler(AbsTaskWrapper wrapper) { RecordHandler recordHandler = new RecordHandler(wrapper); - HttpRecordAdapter adapter = new HttpRecordAdapter(wrapper); + HttpRecordHandler adapter = new HttpRecordHandler(wrapper); recordHandler.setAdapter(adapter); return recordHandler; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java index 5fd2acd0..8ef1e78e 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java @@ -20,7 +20,7 @@ import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.AbsLoader; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -142,7 +142,7 @@ public abstract class BaseM3U8Loader extends AbsLoader { @Override protected IRecordHandler getRecordHandler(AbsTaskWrapper wrapper) { RecordHandler handler = new RecordHandler(wrapper); - M3U8RecordAdapter adapter = new M3U8RecordAdapter((DTaskWrapper) wrapper); + M3U8RecordHandler adapter = new M3U8RecordHandler((DTaskWrapper) wrapper); handler.setAdapter(adapter); return handler; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java similarity index 95% rename from M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java index 17cc04e3..4eb15411 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java @@ -17,11 +17,11 @@ package com.arialyy.aria.m3u8; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; -import com.arialyy.aria.core.common.AbsRecordHandlerAdapter; +import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; @@ -33,10 +33,10 @@ import java.util.ArrayList; * @Author lyy * @Date 2019-09-24 */ -public class M3U8RecordAdapter extends AbsRecordHandlerAdapter { +public class M3U8RecordHandler extends RecordHandler { private M3U8TaskOption mOption; - M3U8RecordAdapter(DTaskWrapper wrapper) { + M3U8RecordHandler(DTaskWrapper wrapper) { super(wrapper); mOption = (M3U8TaskOption) wrapper.getM3u8Option(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java deleted file mode 100644 index ee74b0e4..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsRecordHandlerAdapter.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.common; - -import com.arialyy.aria.core.inf.IRecordHandlerAdapter; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.util.CommonUtil; - -/** - * @Author lyy - * @Date 2019-09-19 - */ -public abstract class AbsRecordHandlerAdapter implements IRecordHandlerAdapter { - private AbsTaskWrapper mWrapper; - protected String TAG = CommonUtil.getClassName(getClass()); - - @Override public void onPre() { - } - - public AbsRecordHandlerAdapter(AbsTaskWrapper wrapper) { - mWrapper = wrapper; - } - - public AbsTaskWrapper getWrapper() { - return mWrapper; - } - - protected AbsNormalEntity getEntity() { - return (AbsNormalEntity) getWrapper().getEntity(); - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java index dfe8f2d7..7f8eac62 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHandler.java @@ -18,8 +18,8 @@ package com.arialyy.aria.core.common; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IRecordHandler; -import com.arialyy.aria.core.inf.IRecordHandlerAdapter; +import com.arialyy.aria.core.loader.ILoaderVisitor; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; @@ -38,22 +38,29 @@ import java.util.Set; /** * 处理任务记录,分配线程区间 */ -public class RecordHandler implements IRecordHandler { - private final String TAG = "RecordHandler"; +public abstract class RecordHandler implements IRecordHandler { + protected final String TAG = CommonUtil.getClassName(this); @Deprecated private File mConfigFile; private TaskRecord mTaskRecord; private AbsTaskWrapper mTaskWrapper; private AbsNormalEntity mEntity; - private IRecordHandlerAdapter mAdapter; public RecordHandler(AbsTaskWrapper wrapper) { mTaskWrapper = wrapper; mEntity = (AbsNormalEntity) mTaskWrapper.getEntity(); } - public void setAdapter(IRecordHandlerAdapter mAdapter) { - this.mAdapter = mAdapter; + public AbsTaskWrapper getWrapper() { + return mTaskWrapper; + } + + public AbsNormalEntity getEntity() { + return mEntity; + } + + @Override public void onPre() { + } /** @@ -70,10 +77,10 @@ public class RecordHandler implements IRecordHandler { if (mConfigFile.exists()) { convertDb(); } else { - mAdapter.onPre(); + onPre(); mTaskRecord = DbDataHelper.getTaskRecord(getFilePath(), mEntity.getTaskType()); if (mTaskRecord == null) { - if (!new File(getFilePath()).exists()){ + if (!new File(getFilePath()).exists()) { FileUtil.createFile(getFilePath()); } initRecord(true); @@ -83,14 +90,14 @@ public class RecordHandler implements IRecordHandler { ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); mTaskRecord.threadRecords.clear(); - mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); + mTaskRecord.threadNum = initTaskThreadNum(); initRecord(false); } else if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { - mTaskRecord.threadNum = mAdapter.initTaskThreadNum(); + mTaskRecord.threadNum = initTaskThreadNum(); initRecord(false); } } - mAdapter.handlerTaskRecord(mTaskRecord); + handlerTaskRecord(mTaskRecord); } saveRecord(); return mTaskRecord; @@ -127,7 +134,7 @@ public class RecordHandler implements IRecordHandler { return; } mTaskWrapper.setNewTask(false); - mTaskRecord = mAdapter.createTaskRecord(threadNum); + mTaskRecord = createTaskRecord(threadNum); mTaskRecord.isBlock = false; File tempFile = new File(getFilePath()); for (int i = 0; i < threadNum; i++) { @@ -158,7 +165,7 @@ public class RecordHandler implements IRecordHandler { */ private void initRecord(boolean newRecord) { if (newRecord) { - mTaskRecord = mAdapter.createTaskRecord(mAdapter.initTaskThreadNum()); + mTaskRecord = createTaskRecord(initTaskThreadNum()); } mTaskWrapper.setNewTask(true); int requestType = mTaskWrapper.getRequestType(); @@ -169,7 +176,7 @@ public class RecordHandler implements IRecordHandler { // 处理线程区间记录 for (int i = 0; i < mTaskRecord.threadNum; i++) { long startL = i * blockSize, endL = (i + 1) * blockSize; - ThreadRecord tr = mAdapter.createThreadRecord(mTaskRecord, i, startL, endL); + ThreadRecord tr = createThreadRecord(mTaskRecord, i, startL, endL); mTaskRecord.threadRecords.add(tr); } } @@ -198,4 +205,23 @@ public class RecordHandler implements IRecordHandler { return ((UploadEntity) mTaskWrapper.getEntity()).getFilePath(); } } + + @Override public void accept(ILoaderVisitor visitor) { + visitor.addComponent(this); + } + + @Override public boolean checkTaskCompleted() { + if (mTaskRecord == null + || mTaskRecord.threadRecords == null + || mTaskRecord.threadRecords.isEmpty()) { + return false; + } + int completeNum = 0; + for (ThreadRecord tr : mTaskRecord.threadRecords) { + if (tr.isComplete) { + completeNum++; + } + } + return completeNum != 0 && completeNum == mTaskRecord.threadNum; + } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 2975dc29..9b1ee2ef 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.common; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java index 4b17fea4..49fcbba8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java @@ -62,13 +62,13 @@ public class XMLReader extends DefaultHandler { String value = attributes.getValue("value"); switch (qName) { - case "threadNum": // 线程数 + case "getCreatedThreadNum": // 线程数 int threadNum = checkInt(value) ? Integer.parseInt(value) : 3; if (threadNum < 1) { ALog.w(TAG, "下载线程数不能小于 1"); threadNum = 1; } - setField("threadNum", threadNum, ConfigType.DOWNLOAD); + setField("getCreatedThreadNum", threadNum, ConfigType.DOWNLOAD); break; case "maxTaskNum": //最大任务书 int maxTaskNum = checkInt(value) ? Integer.parseInt(value) : 2; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java similarity index 98% rename from PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java index b216b003..1f0ba27e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java @@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit; * Created by AriaL on 2017/6/30. * 任务组核心逻辑 */ -public abstract class AbsGroupUtil implements IUtil, Runnable { +public abstract class AbsGroupLoaderUtil implements IUtil, Runnable { protected final String TAG = CommonUtil.getClassName(getClass()); private long mCurrentLocation = 0; @@ -52,7 +52,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { private DGTaskWrapper mGTWrapper; private GroupRunState mState; - protected AbsGroupUtil(AbsTaskWrapper groupWrapper, IEventListener listener) { + protected AbsGroupLoaderUtil(AbsTaskWrapper groupWrapper, IEventListener listener) { mListener = (IDGroupListener) listener; mGTWrapper = (DGTaskWrapper) groupWrapper; mUpdateInterval = Configuration.getInstance().downloadCfg.getUpdateInterval(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java index f4bbf9df..0f8beecb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java @@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit; /** * 组合任务子任务调度器,用于调度任务的开始、停止、失败、完成等情况 - * 该调度器生命周期和{@link AbsGroupUtil}生命周期一致 + * 该调度器生命周期和{@link AbsGroupLoaderUtil}生命周期一致 */ class SimpleSchedulers implements ISchedulers { private static final String TAG = "SimpleSchedulers"; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java index c4f87b70..65bec3af 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.Set; /** - * 组合任务队列,该队列生命周期和{@link AbsGroupUtil}生命周期一致 + * 组合任务队列,该队列生命周期和{@link AbsGroupLoaderUtil}生命周期一致 */ class SimpleSubQueue implements ISubQueue { private static final String TAG = "SimpleSubQueue"; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java index b48bbc47..f0af12ce 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadState.java @@ -16,11 +16,14 @@ package com.arialyy.aria.core.inf; import android.os.Handler; +import android.os.Looper; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.loader.ILoaderComponent; /** * 线程任务状态 */ -public interface IThreadState extends Handler.Callback { +public interface IThreadState extends ILoaderComponent { int STATE_STOP = 0x01; int STATE_FAIL = 0x02; int STATE_CANCEL = 0x03; @@ -50,4 +53,14 @@ public interface IThreadState extends Handler.Callback { * @return 任务当前进度 */ long getCurrentProgress(); + + /** + * 设置消息循环体 + */ + void setLooper(TaskRecord taskRecord, Looper looper); + + /** + * 创建handler 回调 + */ + Handler.Callback getHandlerCallback(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java index fa9b6a51..1df88fd8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java @@ -19,7 +19,7 @@ import android.os.Handler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.util.CommonUtil; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java index b7ea9108..104003fd 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java @@ -17,7 +17,7 @@ package com.arialyy.aria.core.listener; import android.os.Handler; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.upload.UTaskWrapper; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java index 77db34a3..f15167c2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java @@ -21,7 +21,7 @@ import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.task.DownloadGroupTask; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java index c74e8c58..28618e32 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsLoader.java @@ -16,9 +16,7 @@ package com.arialyy.aria.core.loader; import android.os.Looper; -import android.util.SparseArray; import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.manager.ThreadTaskManager; @@ -27,20 +25,27 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Created by AriaL on 2017/7/1. - * 控制线程任务状态,如:开始,停止,取消,重试 + * 任务执行器,用于处理任务的开始,停止 + * 流程: + * 1、获取任务记录 + * 2、创建任务状态管理器,用于管理任务的状态 + * 3、创建文件信息获取器,获取文件信息,根据文件信息执行任务 + * 4、创建线程任务执行下载、上传操作 */ -public abstract class AbsLoader implements Runnable { +public abstract class AbsLoader implements ILoaderVisitor, ILoader { protected final String TAG; protected IEventListener mListener; protected AbsTaskWrapper mTaskWrapper; protected File mTempFile; - private SparseArray mTask = new SparseArray<>(); + private List mTask = new ArrayList<>(); private ScheduledThreadPoolExecutor mTimer; /** @@ -48,20 +53,23 @@ public abstract class AbsLoader implements Runnable { */ private long mUpdateInterval = 1000; protected TaskRecord mRecord; - private IThreadState mStateManager; private boolean isCancel = false, isStop = false; private boolean isRuning = false; + private Looper mLooper; - protected AbsLoader(IEventListener listener, AbsTaskWrapper wrapper) { + protected IRecordHandler mRecordHandler; + protected IThreadState mStateManager; + protected IInfoTask mInfoTask; + protected IThreadTaskBuilder mTTBuilder; + + protected AbsLoader(AbsTaskWrapper wrapper, IEventListener listener) { mListener = listener; mTaskWrapper = wrapper; TAG = CommonUtil.getClassName(getClass()); } - protected abstract IThreadState createStateManager(Looper looper); - /** - * 处理任务 + * 启动线程任务 */ protected abstract void handleTask(); @@ -70,11 +78,6 @@ public abstract class AbsLoader implements Runnable { */ public abstract long getFileSize(); - /** - * 获取当前任务位置 - */ - public abstract long getCurrentLocation(); - public IThreadState getStateManager() { return mStateManager; } @@ -83,7 +86,7 @@ public abstract class AbsLoader implements Runnable { return mTaskWrapper.getKey(); } - public SparseArray getTaskList() { + public List getTaskList() { return mTask; } @@ -94,16 +97,20 @@ public abstract class AbsLoader implements Runnable { closeTimer(); if (mTask != null && mTask.size() != 0) { for (int i = 0; i < mTask.size(); i++) { - mTask.valueAt(i).breakTask(); + mTask.get(i).breakTask(); } mTask.clear(); } } - /** - * 任务记录工具 - */ - protected abstract IRecordHandler getRecordHandler(AbsTaskWrapper wrapper); + @Override public void run() { + checkComponent(); + if (isRunning()) { + ALog.d(TAG, String.format("任务【%s】正在执行,启动任务失败", mTaskWrapper.getKey())); + return; + } + startFlow(); + } /** * 开始流程 @@ -114,21 +121,18 @@ public abstract class AbsLoader implements Runnable { } isRuning = true; resetState(); - mRecord = getRecordHandler(mTaskWrapper).getRecord(); - Looper.prepare(); - Looper looper = Looper.myLooper(); - mStateManager = createStateManager(looper); onPostPre(); handleTask(); startTimer(); Looper.loop(); } - @Override public void run() { - if (isRunning()) { - return; + @Override public Looper getLooper() { + if (mLooper == null) { + Looper.prepare(); + mLooper = Looper.myLooper(); } - startFlow(); + return mLooper; } /** @@ -212,7 +216,7 @@ public abstract class AbsLoader implements Runnable { isCancel = true; onCancel(); for (int i = 0; i < mTask.size(); i++) { - IThreadTask task = mTask.valueAt(i); + IThreadTask task = mTask.get(i); if (task != null && !task.isThreadComplete()) { task.cancel(); } @@ -245,7 +249,7 @@ public abstract class AbsLoader implements Runnable { isStop = true; onStop(); for (int i = 0; i < mTask.size(); i++) { - IThreadTask task = mTask.valueAt(i); + IThreadTask task = mTask.get(i); if (task != null && !task.isThreadComplete()) { task.stop(); } @@ -253,7 +257,7 @@ public abstract class AbsLoader implements Runnable { ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey()); onPostStop(); onDestroy(); - mListener.onStop(getCurrentLocation()); + mListener.onStop(getCurrentProgress()); } /** @@ -270,17 +274,6 @@ public abstract class AbsLoader implements Runnable { } - /** - * 直接调用的时候会自动启动线程执行 - */ - public synchronized void start() { - if (isRunning()) { - ALog.d(TAG, String.format("任务【%s】正在执行,启动任务失败", mTaskWrapper.getKey())); - return; - } - new Thread(this).start(); - } - /** * 重试任务 */ @@ -303,4 +296,22 @@ public abstract class AbsLoader implements Runnable { } return false; } + + /** + * 检查组件: {@link #mRecordHandler}、{@link #mInfoTask}、{@link #mStateManager}、{@link #mTTBuilder} + */ + private void checkComponent() { + if (mRecordHandler == null) { + throw new NullPointerException("任务记录组件为空"); + } + if (mInfoTask == null) { + throw new NullPointerException(("文件信息组件为空")); + } + if (mStateManager == null) { + throw new NullPointerException("任务状态管理组件为空"); + } + if (mTTBuilder == null) { + throw new NullPointerException("线程任务组件为空"); + } + } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java index 1ca1ec5b..e31b0c81 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java @@ -29,19 +29,25 @@ import com.arialyy.aria.util.CommonUtil; public abstract class AbsNormalLoaderUtil implements IUtil { protected String TAG = CommonUtil.getClassName(getClass()); private IEventListener mListener; - private AbsLoader mLoader; + protected AbsLoader mLoader; private AbsTaskWrapper mTaskWrapper; private boolean isStop = false, isCancel = false; protected AbsNormalLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) { mTaskWrapper = wrapper; mListener = listener; - mLoader = createLoader(); + mLoader = getLoader(); } - public AbsLoader getLoader() { - return mLoader; - } + /** + * 获取加载器 + */ + public abstract AbsLoader getLoader(); + + /** + * 获取构造器 + */ + public abstract LoaderStructure getLoaderStructure(); @Override public String getKey() { return mTaskWrapper.getKey(); @@ -55,7 +61,7 @@ public abstract class AbsNormalLoaderUtil implements IUtil { * 获取当前下载位置 */ @Override public long getCurrentLocation() { - return mLoader.getCurrentLocation(); + return mLoader.getCurrentProgress(); } @Override public boolean isRunning() { @@ -105,10 +111,10 @@ public abstract class AbsNormalLoaderUtil implements IUtil { //} else { // mDownloader.create(); //} - Runnable runnable = createInfoThread(); - if (runnable != null) { - new Thread(runnable).start(); - } + + getLoaderStructure(); + new Thread(mLoader).start(); + onStart(); } @@ -139,14 +145,4 @@ public abstract class AbsNormalLoaderUtil implements IUtil { public AbsTaskWrapper getTaskWrapper() { return mTaskWrapper; } - - /** - * 创建加载器的适配器 - */ - protected abstract AbsLoader createLoader(); - - /** - * 通过链接类型创建不同的获取文件信息的线程 - */ - protected abstract Runnable createInfoThread(); } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java new file mode 100644 index 00000000..a483ecd3 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java @@ -0,0 +1,185 @@ +package com.arialyy.aria.core.loader; + +import android.os.Handler; +import android.os.Looper; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.AbsNormalEntity; +import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.download.DGTaskWrapper; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.task.IThreadTaskAdapter; +import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder { + protected String TAG = CommonUtil.getClassName(this); + + private Handler mStateHandler; + private AbsTaskWrapper mWrapper; + private TaskRecord mRecord; + private int mTotalThreadNum; + private File mTempFile; + private int mStartThreadNum; + + public AbsNormalTTBuilder(AbsTaskWrapper wrapper) { + if (wrapper instanceof DGTaskWrapper) { + throw new AssertionError("NormalTTBuilder 不适用于组合任务"); + } + mWrapper = wrapper; + mTempFile = new File(((AbsNormalEntity) wrapper.getEntity()).getFilePath()); + } + + protected File getTempFile(){ + return mTempFile; + } + + protected AbsNormalEntity getEntity() { + return (AbsNormalEntity) mWrapper.getEntity(); + } + + /** + * 创建线程任务适配器 + */ + public abstract IThreadTaskAdapter getAdapter(SubThreadConfig config); + + /** + * 处理新任务 + * + * @param record 任务记录 + * @param totalThreadNum 任务的线程总数 + * @return {@code true}创建新任务成功 + */ + public abstract boolean handleNewTask(TaskRecord record, int totalThreadNum); + + /** + * 创建线程任务 + */ + private IThreadTask createThreadTask(SubThreadConfig config) { + ThreadTask task = new ThreadTask(config); + task.setAdapter(getAdapter(config)); + return task; + } + + /** + * 启动断点任务时,创建单线程任务 + * + * @param record 线程记录 + * @param startNum 启动的线程数 + */ + private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) { + SubThreadConfig config = new SubThreadConfig(); + config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl(); + config.tempFile = + mRecord.isBlock ? new File( + String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) + : mTempFile; + config.isBlock = mRecord.isBlock; + config.startThreadNum = startNum; + config.taskWrapper = mWrapper; + config.record = record; + config.stateHandler = mStateHandler; + return createThreadTask(config); + } + + /** + * 处理不支持断点的任务 + */ + private List handleNoSupportBP() { + List list = new ArrayList<>(); + mStartThreadNum = 1; + + IThreadTask task = createSingThreadTask(mRecord.threadRecords.get(0), 1); + if (task == null) { + ALog.e(TAG, "创建线程任务失败"); + return null; + } + list.add(task); + return list; + } + + /** + * 处理支持断点的任务 + */ + private List handleBreakpoint() { + long fileLength = getEntity().getFileSize(); + long blockSize = fileLength / mTotalThreadNum; + long currentProgress = 0; + List threadTasks = new ArrayList<>(); + + mRecord.fileLength = fileLength; + if (mWrapper.isNewTask() && !handleNewTask(mRecord, mTotalThreadNum)) { + ALog.e(TAG, "初始化线程任务失败"); + return null; + } + + for (ThreadRecord tr : mRecord.threadRecords) { + if (!tr.isComplete) { + mStartThreadNum++; + } + } + + for (int i = 0; i < mTotalThreadNum; i++) { + long startL = i * blockSize, endL = (i + 1) * blockSize; + ThreadRecord tr = mRecord.threadRecords.get(i); + + if (tr.isComplete) {//该线程已经完成 + currentProgress += endL - startL; + ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i)); + mStateHandler.obtainMessage(IThreadState.STATE_COMPLETE).sendToTarget(); + continue; + } + + //如果有记录,则恢复任务 + long r = tr.startLocation; + //记录的位置需要在线程区间中 + if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) { + currentProgress += r - startL; + } + ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", getEntity().getFileName(), i)); + + IThreadTask task = createSingThreadTask(tr, mStartThreadNum); + if (task == null) { + ALog.e(TAG, "创建线程任务失败"); + return null; + } + threadTasks.add(task); + } + if (currentProgress != 0 && currentProgress != getEntity().getCurrentProgress()) { + ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); + getEntity().setCurrentProgress(currentProgress); + } + //mStateManager.updateProgress(currentProgress); + return threadTasks; + } + + private List handleTask() { + if (mWrapper.isSupportBP()) { + return handleBreakpoint(); + }else { + return handleNoSupportBP(); + } + } + + @Override public List buildThreadTask(TaskRecord record, Looper looper, + IThreadState stateManager) { + mRecord = record; + mStateHandler = new Handler(looper, stateManager.getHandlerCallback()); + mTotalThreadNum = mRecord.threadNum; + return handleTask(); + } + + @Override public int getCreatedThreadNum() { + return mStartThreadNum; + } + + @Override public void accept(ILoaderVisitor visitor) { + visitor.addComponent(this); + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java similarity index 60% rename from PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java index 71eccce9..0e90fb97 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/OnFileInfoCallback.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IInfoTask.java @@ -13,24 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.loader; -import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.common.AbsEntity; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.exception.BaseException; -public interface OnFileInfoCallback { +/** + * 任务信息采集 + */ +public interface IInfoTask extends ILoaderComponent { + /** - * 处理完成 - * - * @param info 一些回调的信息 + * 执行任务 */ - void onComplete(String key, CompleteInfo info); + void run(); /** - * 请求失败 - * - * @param e 错误信息 + * 设置回调 */ - void onFail(AbsEntity entity, BaseException e, boolean needRetry); -} \ No newline at end of file + void setCallback(Callback callback); + + interface Callback { + /** + * 处理完成 + * + * @param info 一些回调的信息 + */ + void onSucceed(String key, CompleteInfo info); + + /** + * 请求失败 + * + * @param e 错误信息 + */ + void onFail(AbsEntity entity, BaseException e, boolean needRetry); + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoader.java index d6bfda0f..356cdc08 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoader.java @@ -15,15 +15,24 @@ */ package com.arialyy.aria.core.loader; -public interface ILoader { +import android.os.Looper; - void start(); +public interface ILoader extends Runnable{ + + //void start(); void stop(); - void isBreak(); + /** + * 任务是否被中断(停止,取消) + * + * @return true 任务中断,false 任务没有中断 + */ + boolean isBreak(); String getKey(); long getCurrentProgress(); + + Looper getLooper(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java index bc865e0e..4408dbd8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderAdapter.java @@ -16,7 +16,6 @@ package com.arialyy.aria.core.loader; import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.common.SubThreadConfig; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderIntercept.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderComponent.java similarity index 63% rename from PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderIntercept.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderComponent.java index dc8582e4..465d4dab 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderIntercept.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderComponent.java @@ -15,33 +15,11 @@ */ package com.arialyy.aria.core.loader; -public class LoaderIntercept implements ILoaderInterceptor, ILoader { - - - - @Override public ILoader intercept(Chain chain) { - - - return this; - } - - @Override public void start() { - - } - - @Override public void stop() { - - } - - @Override public void isBreak() { - - } +/** + * 加载器部件 + */ +public interface ILoaderComponent { - @Override public String getKey() { - return null; - } + void accept(ILoaderVisitor visitor); - @Override public long getCurrentProgress() { - return 0; - } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderInterceptor.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderVisitor.java similarity index 60% rename from PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderInterceptor.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderVisitor.java index a6a4adfc..e4d0b84c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderInterceptor.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ILoaderVisitor.java @@ -15,27 +15,30 @@ */ package com.arialyy.aria.core.loader; -import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.listener.IEventListener; -import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.core.inf.IThreadState; /** - * 拦截器 + * 加载器访问者 */ -public interface ILoaderInterceptor { - - ILoader intercept(Chain chain); - - interface Chain { - - void updateRecord(TaskRecord record); - - TaskRecord getRecord(); - - IEventListener getListener(); - - ITaskWrapper getWrapper(); - - ILoader proceed(); - } +public interface ILoaderVisitor { + + /** + * 处理任务记录 + */ + void addComponent(IRecordHandler recordHandler); + + /** + * 处理任务的文件信息 + */ + void addComponent(IInfoTask infoTask); + + /** + * 线程状态 + */ + void addComponent(IThreadState threadState); + + /** + * 构造线程任务 + */ + void addComponent(IThreadTaskBuilder builder); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java similarity index 69% rename from PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java index e2a2992b..b27b7249 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandlerAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java @@ -13,18 +13,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.loader; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; /** - * 任务记录处理适配器 - * * @Author lyy - * @Date 2019-09-19 + * @Date 2019-09-18 */ -public interface IRecordHandlerAdapter { +public interface IRecordHandler extends ILoaderComponent { + + int TYPE_DOWNLOAD = 1; + int TYPE_UPLOAD = 2; + int TYPE_M3U8_VOD = 3; + int TYPE_M3U8_LIVE = 4; + + String STATE = "_state_"; + String RECORD = "_record_"; + /** + * 小于1m的文件不启用多线程 + */ + long SUB_LEN = 1024 * 1024; + + /** + * 分块文件路径 + */ + String SUB_PATH = "%s.%s.part"; + + /** + * 获取任务记录 + */ + TaskRecord getRecord(); /** * 记录处理前的操作,可用来删除任务记录 @@ -57,4 +77,11 @@ public interface IRecordHandlerAdapter { * @return 新任务的线程数 */ int initTaskThreadNum(); + + /** + * 检查任务是否已完成 + * + * @return true 任务已完成 + */ + boolean checkTaskCompleted(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IThreadTaskBuilder.java similarity index 55% rename from PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/IThreadTaskBuilder.java index 17e75de3..bb2a8aa6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IRecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IThreadTaskBuilder.java @@ -13,35 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.core.inf; +package com.arialyy.aria.core.loader; +import android.os.Looper; import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.inf.IThreadState; +import com.arialyy.aria.core.task.IThreadTask; +import java.util.List; /** - * @Author lyy - * @Date 2019-09-18 + * 线程任务构造器 */ -public interface IRecordHandler { - - int TYPE_DOWNLOAD = 1; - int TYPE_UPLOAD = 2; - int TYPE_M3U8_VOD = 3; - int TYPE_M3U8_LIVE = 4; - - String STATE = "_state_"; - String RECORD = "_record_"; - /** - * 小于1m的文件不启用多线程 - */ - long SUB_LEN = 1024 * 1024; +public interface IThreadTaskBuilder extends ILoaderComponent { /** - * 分块文件路径 + * 构造线程任务 */ - String SUB_PATH = "%s.%s.part"; + List buildThreadTask(TaskRecord record, Looper looper, IThreadState stateManager); /** - * 获取任务记录 + * 获取创建的线程任务数,需要先调用{@link #buildThreadTask(TaskRecord, Looper, IThreadState)}方法才能获取创建的线程任务数 */ - TaskRecord getRecord(); + int getCreatedThreadNum(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderChain.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderChain.java deleted file mode 100644 index 5956e73e..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderChain.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.loader; - -import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.listener.IEventListener; -import com.arialyy.aria.core.wrapper.ITaskWrapper; -import java.util.List; - -/** - * 责任链 - */ -public final class LoaderChain implements ILoaderInterceptor.Chain { - private ITaskWrapper wrapper; - private IEventListener listener; - private TaskRecord taskRecord; - private int index; - private List interceptors; - - public LoaderChain(List interceptors, ITaskWrapper wrapper, - IEventListener listener, TaskRecord taskRecord, - int index) { - this.interceptors = interceptors; - this.wrapper = wrapper; - this.listener = listener; - this.taskRecord = taskRecord; - this.index = index; - } - - @Override public void updateRecord(TaskRecord record) { - this.taskRecord = record; - } - - @Override public TaskRecord getRecord() { - return taskRecord; - } - - @Override public IEventListener getListener() { - return listener; - } - - @Override public ITaskWrapper getWrapper() { - return wrapper; - } - - @Override public ILoader proceed() { - int index = this.index + 1; - if (index >= interceptors.size()) { - throw new AssertionError(); - } - - LoaderChain next = new LoaderChain(interceptors, wrapper, listener, taskRecord, index); - ILoaderInterceptor interceptor = interceptors.get(index); - ILoader loader = interceptor.intercept(next); - - if (loader == null) { - throw new NullPointerException("Loader为空"); - } - - return loader; - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/RecordInterceptor.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderStructure.java similarity index 50% rename from PublicComponent/src/main/java/com/arialyy/aria/core/loader/RecordInterceptor.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderStructure.java index 98c0f0bd..5454f294 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/RecordInterceptor.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/LoaderStructure.java @@ -15,25 +15,31 @@ */ package com.arialyy.aria.core.loader; -import com.arialyy.aria.core.common.RecordHandler; -import com.arialyy.aria.core.inf.IRecordHandlerAdapter; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.inf.IThreadState; +import java.util.ArrayList; +import java.util.List; -/** - * 任务记录拦截器,用于处理任务记录 - */ -public final class RecordInterceptor implements ILoaderInterceptor { - private IRecordHandlerAdapter adapter; +public class LoaderStructure { + private List parts = new ArrayList<>(); - public RecordInterceptor(IRecordHandlerAdapter adapter) { - this.adapter = adapter; - } + public void accept(ILoaderVisitor visitor) { - @Override public ILoader intercept(Chain chain) { - RecordHandler recordHandler = new RecordHandler((AbsTaskWrapper) chain.getWrapper()); - recordHandler.setAdapter(adapter); - chain.updateRecord(recordHandler.getRecord()); + for (ILoaderComponent part : parts) { + part.accept(visitor); + } + } - return chain.proceed(); + /** + * 将组件加入到集合,必须添加以下集合: + * 1 {@link IRecordHandler} + * 2 {@link IInfoTask} + * 3 {@link IThreadState} + * 4 {@link IThreadTaskBuilder} + * + * @param component 待添加的组件 + */ + public LoaderStructure addComponent(ILoaderComponent component) { + parts.add(component); + return this; } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index 6b113f2e..50755733 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -15,20 +15,17 @@ */ package com.arialyy.aria.core.loader; -import android.os.Handler; -import android.os.Looper; -import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.AbsNormalEntity; -import com.arialyy.aria.core.common.SubThreadConfig; +import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.event.EventMsgUtil; -import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.IThreadState; -import com.arialyy.aria.core.listener.BaseDListener; import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import java.io.File; @@ -36,23 +33,15 @@ import java.io.File; * 单文件 */ public class NormalLoader extends AbsLoader { - private ThreadStateManager mStateManager; - private Handler mStateHandler; - protected int mTotalThreadNum; //总线程数 private int mStartThreadNum; //启动的线程数 - private ILoaderAdapter mAdapter; - public NormalLoader(IEventListener listener, AbsTaskWrapper wrapper) { - super(listener, wrapper); + public NormalLoader(AbsTaskWrapper wrapper, IEventListener listener) { + super(wrapper, listener); mTempFile = new File(getEntity().getFilePath()); EventMsgUtil.getDefault().register(this); setUpdateInterval(wrapper.getConfig().getUpdateInterval()); } - public void setAdapter(ILoaderAdapter adapter) { - mAdapter = adapter; - } - public AbsNormalEntity getEntity() { return (AbsNormalEntity) mTaskWrapper.getEntity(); } @@ -61,24 +50,15 @@ public class NormalLoader extends AbsLoader { return getEntity().getFileSize(); } - @Override public long getCurrentLocation() { - return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress(); - } - - @Override protected IRecordHandler getRecordHandler(AbsTaskWrapper wrapper) { - return mAdapter.recordHandler(wrapper); - } - /** - * 设置最大下载/上传速度 + * 设置最大下载/上传速度AbsFtpInfoThread * * @param maxSpeed 单位为:kb */ protected void setMaxSpeed(int maxSpeed) { - for (int i = 0; i < getTaskList().size(); i++) { - IThreadTask task = getTaskList().valueAt(i); - if (task != null && mStartThreadNum > 0) { - task.setMaxSpeed(maxSpeed / mStartThreadNum); + for (IThreadTask threadTask : getTaskList()) { + if (threadTask != null && mStartThreadNum > 0) { + threadTask.setMaxSpeed(maxSpeed / mStartThreadNum); } } } @@ -90,11 +70,6 @@ public class NormalLoader extends AbsLoader { @Override protected void onPostPre() { super.onPostPre(); - if (mAdapter == null) { - throw new NullPointerException("请使用adapter设置适配器"); - } - mTotalThreadNum = mRecord.threadNum; - if (mListener instanceof IDLoadListener) { ((IDLoadListener) mListener).onPostPre(getEntity().getFileSize()); } @@ -114,126 +89,63 @@ public class NormalLoader extends AbsLoader { } } - @Override protected IThreadState createStateManager(Looper looper) { - mStateManager = new ThreadStateManager(looper, mRecord, mListener); - mStateHandler = new Handler(looper, mStateManager); - return mStateManager; - } - - @Override protected void handleTask() { - if (mTaskWrapper.isSupportBP()) { - handleBreakpoint(); - } else { - handleNoSupportBP(); - } - } - /** - * 启动断点任务时,创建单线程任务 - * - * @param record 线程记录 - * @param startNum 启动的线程数 + * 启动单线程任务 */ - private IThreadTask createSingThreadTask(ThreadRecord record, int startNum) { - SubThreadConfig config = new SubThreadConfig(); - config.url = getEntity().isRedirect() ? getEntity().getRedirectUrl() : getEntity().getUrl(); - config.tempFile = - mRecord.isBlock ? new File( - String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId)) - : mTempFile; - config.isBlock = mRecord.isBlock; - config.startThreadNum = startNum; - config.taskWrapper = mTaskWrapper; - config.record = record; - config.stateHandler = mStateHandler; - return mAdapter.createThreadTask(config); - } - - private void handleBreakpoint() { - long fileLength = getEntity().getFileSize(); - long blockSize = fileLength / mTotalThreadNum; - long currentProgress = 0; - - mRecord.fileLength = fileLength; - if (mTaskWrapper.isNewTask() && !mAdapter.handleNewTask(mRecord, mTotalThreadNum)) { - closeTimer(); - mListener.onFail(false, null); + @Override + public void handleTask() { + if (isBreak()) { return; } - - for (ThreadRecord tr : mRecord.threadRecords) { - if (!tr.isComplete) { - mStartThreadNum++; - } - } - - for (int i = 0; i < mTotalThreadNum; i++) { - long startL = i * blockSize, endL = (i + 1) * blockSize; - ThreadRecord tr = mRecord.threadRecords.get(i); - - if (tr.isComplete) {//该线程已经完成 - currentProgress += endL - startL; - ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mTaskWrapper.getKey(), i)); - mStateHandler.obtainMessage(IThreadState.STATE_COMPLETE).sendToTarget(); - if (mStateManager.isComplete()) { - mRecord.deleteData(); - mListener.onComplete(); - return; - } - continue; - } - - //如果有记录,则恢复任务 - long r = tr.startLocation; - //记录的位置需要在线程区间中 - if (startL < r && r <= (i == (mTotalThreadNum - 1) ? fileLength : endL)) { - currentProgress += r - startL; - } - ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", getEntity().getFileName(), i)); - - IThreadTask task = createSingThreadTask(tr, mStartThreadNum); - if (task == null) return; - getTaskList().put(tr.threadId, task); - } - if (currentProgress != 0 && currentProgress != getEntity().getCurrentProgress()) { - ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); - getEntity().setCurrentProgress(currentProgress); - } - mStateManager.updateProgress(currentProgress); - startThreadTask(); + mStateManager.setLooper(mRecord, getLooper()); + mInfoTask.run(); } - /** - * 启动单线程任务 - */ private void startThreadTask() { - if (isBreak()) { - return; - } + getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, getLooper(), mStateManager)); + mStartThreadNum = mTTBuilder.getCreatedThreadNum(); if (mStateManager.getCurrentProgress() > 0) { mListener.onResume(mStateManager.getCurrentProgress()); } else { mListener.onStart(mStateManager.getCurrentProgress()); } - for (int i = 0; i < getTaskList().size(); i++) { - ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), getTaskList().valueAt(i)); + for (IThreadTask threadTask : getTaskList()) { + ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask); } } - /** - * 处理不支持断点的任务 - */ - private void handleNoSupportBP() { - if (mListener instanceof BaseDListener) { - ((BaseDListener) mListener).supportBreakpoint(false); + @Override public long getCurrentProgress() { + return isRunning() ? mStateManager.getCurrentProgress() : getEntity().getCurrentProgress(); + } + + @Override public void addComponent(IRecordHandler recordHandler) { + mRecordHandler = recordHandler; + mRecord = mRecordHandler.getRecord(); + if (recordHandler.checkTaskCompleted()) { + mRecord.deleteData(); + mListener.onComplete(); } - mStartThreadNum = 1; + } + + @Override public void addComponent(IInfoTask infoTask) { + mInfoTask = infoTask; + infoTask.setCallback(new IInfoTask.Callback() { + @Override public void onSucceed(String key, CompleteInfo info) { + startThreadTask(); + } + + @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { + mListener.onFail(needRetry, e); + } + }); + } + + @Override public void addComponent(IThreadState threadState) { + mStateManager = threadState; + } - IThreadTask task = createSingThreadTask(mRecord.threadRecords.get(0), 1); - if (task == null) return; - getTaskList().put(0, task); - ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task); - mListener.onStart(0); + @Override public void addComponent(IThreadTaskBuilder builder) { + mTTBuilder = builder; } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java index c3fd33d8..e8bcf1b5 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java @@ -16,10 +16,10 @@ package com.arialyy.aria.core.loader; import android.os.Bundle; +import android.os.Handler; import android.os.Looper; import android.os.Message; import com.arialyy.aria.core.TaskRecord; -import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.exception.BaseException; @@ -49,76 +49,90 @@ public class ThreadStateManager implements IThreadState { private Looper mLooper; /** - * @param taskRecord 任务记录 * @param listener 任务事件 */ - ThreadStateManager(Looper looper, TaskRecord taskRecord, IEventListener listener) { - mLooper = looper; + public ThreadStateManager(IEventListener listener) { + mListener = listener; + } + + @Override public void setLooper(TaskRecord taskRecord, Looper looper) { mTaskRecord = taskRecord; mThreadNum = mTaskRecord.threadNum; - mListener = listener; + mLooper = looper; } - /** - * 不要使用handle更新启动线程的进度,因为有延迟 - */ - void updateProgress(long curProgress) { - mProgress = curProgress; + private void checkLooper(){ + if (mTaskRecord == null){ + throw new NullPointerException("任务记录为空"); + } + if (mLooper == null){ + throw new NullPointerException("Looper为空"); + } } - @Override public boolean handleMessage(Message msg) { - switch (msg.what) { - case STATE_STOP: - mStopNum++; - if (isStop()) { - quitLooper(); - } - break; - case STATE_CANCEL: - mCancelNum++; - if (isCancel()) { - quitLooper(); - } - break; - case STATE_FAIL: - mFailNum++; - if (isFail()) { - Bundle b = msg.getData(); - mListener.onFail(b.getBoolean(KEY_RETRY, false), - (BaseException) b.getSerializable(KEY_ERROR_INFO)); - quitLooper(); - } - break; - case STATE_COMPLETE: - mCompleteNum++; - if (isComplete()) { - ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum); - if (mTaskRecord.isBlock) { - if (mergeFile()) { - mListener.onComplete(); + private Handler.Callback callback = new Handler.Callback() { + @Override public boolean handleMessage(Message msg) { + checkLooper(); + switch (msg.what) { + case STATE_STOP: + mStopNum++; + if (isStop()) { + quitLooper(); + } + break; + case STATE_CANCEL: + mCancelNum++; + if (isCancel()) { + quitLooper(); + } + break; + case STATE_FAIL: + mFailNum++; + if (isFail()) { + Bundle b = msg.getData(); + mListener.onFail(b.getBoolean(KEY_RETRY, false), + (BaseException) b.getSerializable(KEY_ERROR_INFO)); + quitLooper(); + } + break; + case STATE_COMPLETE: + mCompleteNum++; + if (isComplete()) { + ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum); + if (mTaskRecord.isBlock) { + if (mergeFile()) { + mListener.onComplete(); + } else { + mListener.onFail(false, null); + } } else { - mListener.onFail(false, null); + mListener.onComplete(); } - } else { - mListener.onComplete(); + quitLooper(); } - quitLooper(); - } - break; - case STATE_RUNNING: - if (msg.obj instanceof Long) { - mProgress += (long) msg.obj; - } - break; - case STATE_UPDATE_PROGRESS: - if (msg.obj == null) { - mProgress = updateBlockProgress(); - } else if (msg.obj instanceof Long) { - mProgress = (long) msg.obj; - } - break; + break; + case STATE_RUNNING: + if (msg.obj instanceof Long) { + mProgress += (long) msg.obj; + } + break; + case STATE_UPDATE_PROGRESS: + if (msg.obj == null) { + mProgress = updateBlockProgress(); + } else if (msg.obj instanceof Long) { + mProgress = (long) msg.obj; + } + break; + } + return false; } - return false; + }; + + /** + * 不要使用handle更新启动线程的进度,因为有延迟 + */ + void updateProgress(long curProgress) { + mProgress = curProgress; } /** @@ -138,6 +152,10 @@ public class ThreadStateManager implements IThreadState { return mProgress; } + @Override public Handler.Callback getHandlerCallback() { + return callback; + } + /** * 所有子线程是否都已经停止 */ @@ -226,4 +244,8 @@ public class ThreadStateManager implements IThreadState { return false; } } + + @Override public void accept(ILoaderVisitor visitor) { + visitor.addComponent(this); + } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java index c8f0918e..215412a7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.task; import com.arialyy.aria.core.download.AbsGroupTaskWrapper; -import com.arialyy.aria.core.group.AbsGroupUtil; +import com.arialyy.aria.core.group.AbsGroupLoaderUtil; /** * Created by AriaL on 2017/6/29. @@ -36,7 +36,7 @@ public abstract class AbsGroupTask */ public void startSubTask(String url) { if (getUtil() != null) { - ((AbsGroupUtil) getUtil()).startSubTask(url); + ((AbsGroupLoaderUtil) getUtil()).startSubTask(url); } } @@ -47,7 +47,7 @@ public abstract class AbsGroupTask */ public void stopSubTask(String url) { if (getUtil() != null) { - ((AbsGroupUtil) getUtil()).stopSubTask(url); + ((AbsGroupLoaderUtil) getUtil()).stopSubTask(url); } } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java index 7af9aca5..364510a6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java @@ -87,4 +87,10 @@ public interface IThreadTask extends Callable { * @return {@code true} 分块分大小正常,{@code false} 分块大小错误 */ boolean checkBlock(); + + /** + * 获取线程id + * @return + */ + int getThreadId(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index d4ea3464..d86fa8b8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -228,6 +228,10 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { return true; } + @Override public int getThreadId() { + return mRecord.threadId; + } + /** * 停止任务 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index d71c5003..35f2c14b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -19,7 +19,7 @@ package com.arialyy.aria.util; import android.text.TextUtils; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.orm.DbEntity; import java.lang.reflect.Modifier; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 08603eb2..45b71aec 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -23,7 +23,7 @@ import com.arialyy.aria.core.common.AbsNormalEntity; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.inf.IRecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.wrapper.RecordWrapper; diff --git a/app/gradle.properties b/app/gradle.properties index 89e0d99e..70172ced 100644 --- a/app/gradle.properties +++ b/app/gradle.properties @@ -4,7 +4,7 @@ # Gradle settings configured through the IDE *will override* # any settings specified in this file. -# For more details on how to configure your build environment visit +# For more details on how to configure your build environment addComponent # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. @@ -13,6 +13,6 @@ # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit +# This option should only be used with decoupled projects. More details, addComponent # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true diff --git a/build.gradle b/build.gradle index 63797bf6..d82e187d 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 381 - versionName = '3.8.1' + versionCode = 382 + versionName = '3.8.2' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName From 6839cba91e76da396a4c8daa1e327b92480d6a40 Mon Sep 17 00:00:00 2001 From: lyy Date: Mon, 30 Dec 2019 08:46:10 +0800 Subject: [PATCH 046/140] Update FtpDownloadModule.java --- .../java/com/arialyy/simple/core/download/FtpDownloadModule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java index ec149ddd..8fa1bcca 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java @@ -49,7 +49,6 @@ public class FtpDownloadModule extends BaseViewModule { LiveData getFtpDownloadInfo(Context context) { //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); //String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; - String url = "ftp://58.210.178.52:21/battery1.0.0.apk"; String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath); singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); From c39bfbdd880e3e84d64fe50c0eeb3d9a41cf6903 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Thu, 2 Jan 2020 21:04:44 +0800 Subject: [PATCH 047/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E6=9E=84loa?= =?UTF-8?q?der=E6=A8=A1=E5=9D=97=E5=90=8E=E5=87=BA=E7=8E=B0=E7=9A=84?= =?UTF-8?q?=E7=BB=84=E5=90=88=E4=BB=BB=E5=8A=A1=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/CheckDGEntityUtil.java | 4 +- .../aria/ftp/download/FtpDGLoader.java | 11 +- .../aria/ftp/download/FtpSubDLoaderUtil.java | 6 +- .../aria/http/download/HttpDGInfoTask.java | 4 +- .../aria/http/download/HttpDGLoader.java | 9 +- .../aria/http/download/HttpDGLoaderUtil.java | 3 +- .../http/download/HttpSubDLoaderUtil.java | 6 +- .../aria/m3u8/live/M3U8LiveLoader.java | 4 +- .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 3 + .../aria/m3u8/vod/VodStateManager.java | 4 +- .../aria/core/common/SubThreadConfig.java | 68 ++++++++ .../aria/core/group/AbsGroupLoader.java | 30 ++-- .../aria/core/group/AbsSubDLoadUtil.java | 21 +-- .../aria/core/group/ChildDLoadListener.java | 146 ------------------ .../aria/core/group/SimpleSchedulers.java | 56 ++++--- .../aria/core/group/SimpleSubQueue.java | 10 +- .../aria/core/inf/IThreadStateManager.java | 6 +- .../core/listener/DownloadGroupListener.java | 30 +++- .../aria/core/listener/IDGroupListener.java | 7 +- .../aria/core/loader/AbsNormalTTBuilder.java | 2 + .../core/loader/NormalThreadStateManager.java | 4 +- .../arialyy/aria/core/loader/SubLoader.java | 84 ++++++++-- .../aria/core/manager/ThreadTaskManager.java | 45 +++++- .../arialyy/aria/core/task/IThreadTask.java | 6 +- .../arialyy/aria/core/task/ThreadTask.java | 51 ++++-- .../aria/core/wrapper/ITaskWrapper.java | 5 +- .../com/arialyy/aria/util/CommonUtil.java | 14 +- .../com/arialyy/aria/util/RecordUtil.java | 2 +- .../download/group/DownloadGroupActivity.java | 9 +- 29 files changed, 389 insertions(+), 261 deletions(-) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java index 2834c5c3..15f698c4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDGEntityUtil.java @@ -116,7 +116,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { @Override public boolean checkEntity() { if (mWrapper.getErrorEvent() != null) { - ALog.e(TAG, String.format("下载失败,%s", mWrapper.getErrorEvent().errorMsg)); + ALog.e(TAG, String.format("操作失败,%s", mWrapper.getErrorEvent().errorMsg)); return false; } @@ -195,7 +195,7 @@ public class CheckDGEntityUtil implements ICheckEntityUtil { */ private boolean checkUrls() { if (mEntity.getUrls().isEmpty()) { - ALog.e(TAG, "下载失败,子任务下载列表为null"); + ALog.e(TAG, "操作失败,子任务下载列表为null"); return false; } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGLoader.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGLoader.java index 3fe51b57..33adbf9e 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGLoader.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGLoader.java @@ -45,13 +45,18 @@ final class FtpDGLoader extends AbsGroupLoader { @Override protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) { - return new FtpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo); + return new FtpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo, getKey()); } /** * 启动子任务下载 */ private void startSub() { + if (isBreak()) { + return; + } + onPostStart(); + // ftp需要获取完成只任务信息才更新只任务数量 getState().setSubSize(getWrapper().getSubTaskWrapper().size()); @@ -70,12 +75,12 @@ final class FtpDGLoader extends AbsGroupLoader { startSub(); } else { ALog.e(TAG, "获取任务信息失败,code:" + info.code); - mListener.onFail(false, null); + getListener().onFail(false, null); } } @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - mListener.onFail(needRetry, e); + getListener().onFail(needRetry, e); } }); } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpSubDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpSubDLoaderUtil.java index 1617cb18..2aa47541 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpSubDLoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpSubDLoaderUtil.java @@ -31,14 +31,16 @@ final class FtpSubDLoaderUtil extends AbsSubDLoadUtil { * @param schedulers 调度器 * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 */ - FtpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo) { - super(taskWrapper, schedulers, needGetInfo); + FtpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo, + String parentKey) { + super(taskWrapper, schedulers, needGetInfo, parentKey); } @Override protected SubLoader getLoader() { if (mDLoader == null) { mDLoader = new SubLoader(getWrapper(), getSchedulers()); mDLoader.setNeedGetInfo(isNeedGetInfo()); + mDLoader.setParentKey(getParentKey()); } return mDLoader; } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java index 2e4d9123..e93e9f1e 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java @@ -66,7 +66,7 @@ public final class HttpDGInfoTask implements IInfoTask { } }; - public HttpDGInfoTask(DGTaskWrapper wrapper, DownloadGroupListener listener) { + HttpDGInfoTask(DGTaskWrapper wrapper, DownloadGroupListener listener) { this.wrapper = wrapper; this.listener = listener; } @@ -76,7 +76,7 @@ public final class HttpDGInfoTask implements IInfoTask { if (mPool != null && !getLenComplete) { ALog.d(TAG, "获取长度未完成的情况下,停止组合任务"); mPool.shutdown(); - //mListener.onStop(0); + listener.onStop(0); return; } // 处理组合任务大小未知的情况 diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoader.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoader.java index c18c788f..14d4d201 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoader.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoader.java @@ -22,7 +22,7 @@ import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.group.AbsGroupLoader; import com.arialyy.aria.core.group.AbsSubDLoadUtil; -import com.arialyy.aria.core.listener.IEventListener; +import com.arialyy.aria.core.listener.DownloadGroupListener; import com.arialyy.aria.core.loader.IInfoTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; @@ -31,7 +31,7 @@ import com.arialyy.aria.exception.BaseException; * http 组合任务加载器 */ final class HttpDGLoader extends AbsGroupLoader { - HttpDGLoader(AbsTaskWrapper groupWrapper, IEventListener listener) { + HttpDGLoader(AbsTaskWrapper groupWrapper, DownloadGroupListener listener) { super(groupWrapper, listener); } @@ -44,13 +44,14 @@ final class HttpDGLoader extends AbsGroupLoader { @Override protected AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo) { - return new HttpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo); + return new HttpSubDLoaderUtil(wrapper, getScheduler(), needGetFileInfo, getKey()); } private void startSub() { if (isBreak()) { return; } + onPostStart(); for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) { DownloadEntity dEntity = wrapper.getEntity(); startSubLoader(createSubLoader(wrapper, dEntity.getFileSize() < 0)); @@ -65,7 +66,7 @@ final class HttpDGLoader extends AbsGroupLoader { } @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - mListener.onFail(needRetry, e); + getListener().onFail(needRetry, e); } }); } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoaderUtil.java index c53f07ad..2777ddcc 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGLoaderUtil.java @@ -36,7 +36,8 @@ public final class HttpDGLoaderUtil extends AbsGroupLoaderUtil { } @Override protected AbsGroupLoader getLoader() { - return mLoader == null ? new HttpDGLoader(getTaskWrapper(), getListener()) : mLoader; + return mLoader == null ? new HttpDGLoader(getTaskWrapper(), + (DownloadGroupListener) getListener()) : mLoader; } @Override protected LoaderStructure buildLoaderStructure() { diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java index 0e4d54d9..a7079691 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpSubDLoaderUtil.java @@ -32,14 +32,16 @@ final class HttpSubDLoaderUtil extends AbsSubDLoadUtil { * @param schedulers 调度器 * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 */ - HttpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo) { - super(taskWrapper, schedulers, needGetInfo); + HttpSubDLoaderUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo, + String parentKey) { + super(taskWrapper, schedulers, needGetInfo, parentKey); } @Override protected SubLoader getLoader() { if (mDLoader == null) { mDLoader = new SubLoader(getWrapper(), getSchedulers()); mDLoader.setNeedGetInfo(isNeedGetInfo()); + mDLoader.setParentKey(getParentKey()); } return mDLoader; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 72cbb938..0bd13449 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -31,6 +31,7 @@ import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.processor.ILiveTsUrlConverter; import com.arialyy.aria.core.processor.ITsMergeHandler; import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; import com.arialyy.aria.exception.TaskException; @@ -180,7 +181,8 @@ final class M3U8LiveLoader extends BaseM3U8Loader { config.record = record; config.stateHandler = mStateHandler; config.peerIndex = indexId; - + config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE); + config.updateInterval = SubThreadConfig.getUpdateInterval(ITaskWrapper.M3U8_LIVE); if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 23e0de7b..d3f46333 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -34,6 +34,7 @@ import com.arialyy.aria.core.loader.IThreadTaskBuilder; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.core.task.ThreadTask; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.M3U8Exception; import com.arialyy.aria.m3u8.BaseM3U8Loader; @@ -473,6 +474,8 @@ final class M3U8VodLoader extends BaseM3U8Loader { config.record = record; config.stateHandler = mStateHandler; config.peerIndex = index; + config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE); + config.updateInterval = SubThreadConfig.getUpdateInterval(ITaskWrapper.M3U8_LIVE); if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java index ff1b7dbf..cbba8db3 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java @@ -113,8 +113,8 @@ public final class VodStateManager implements IThreadStateManager { if (isFail()) { ALog.d(TAG, String.format("vod任务【%s】失败", loader.getTempFile().getName())); Bundle b = msg.getData(); - listener.onFail(b.getBoolean(KEY_RETRY, true), - (BaseException) b.getSerializable(KEY_ERROR_INFO)); + listener.onFail(b.getBoolean(DATA_RETRY, true), + (BaseException) b.getSerializable(DATA_ERROR_INFO)); quitLooper(); } break; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index 46a351ca..e7846b1b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -16,14 +16,21 @@ package com.arialyy.aria.core.common; import android.os.Handler; +import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import java.io.File; /** * 子线程下载信息类 */ public class SubThreadConfig { + public static final int TYPE_HTTP = 1; + public static final int TYPE_FTP = 2; + public static final int TYPE_M3U8_PEER = 3; + public static final int TYPE_HTTP_DG_SUB = 4; + public static final int TYPE_FTP_DG_SUB = 5; public AbsTaskWrapper taskWrapper; public boolean isBlock = false; @@ -37,4 +44,65 @@ public class SubThreadConfig { public Handler stateHandler; // m3u8切片索引 public int peerIndex; + // 线程任务类型 + public int threadType = TYPE_HTTP; + // 更新间隔,单位:毫秒 + public long updateInterval = 1000; + + /** + * 转换线程任务类型 + * + * @param requestType {@link AbsTaskWrapper#getRequestType()} + * @return {@link #threadType} + */ + public static int getThreadType(int requestType) { + int threadType = SubThreadConfig.TYPE_HTTP; + switch (requestType) { + case ITaskWrapper.D_HTTP: + case ITaskWrapper.U_HTTP: + threadType = SubThreadConfig.TYPE_HTTP; + break; + case ITaskWrapper.D_FTP: + case ITaskWrapper.U_FTP: + threadType = SubThreadConfig.TYPE_FTP; + break; + case ITaskWrapper.D_FTP_DIR: + threadType = SubThreadConfig.TYPE_FTP_DG_SUB; + break; + case ITaskWrapper.DG_HTTP: + threadType = SubThreadConfig.TYPE_HTTP_DG_SUB; + break; + case ITaskWrapper.M3U8_LIVE: + case ITaskWrapper.M3U8_VOD: + threadType = SubThreadConfig.TYPE_M3U8_PEER; + break; + } + return threadType; + } + + /** + * 根据配置肚脐更新间隔 + * + * @param requestType {@link AbsTaskWrapper#getRequestType()} + * @return {@link #updateInterval} + */ + public static long getUpdateInterval(int requestType) { + long updateInterval = 1000; + switch (requestType) { + case ITaskWrapper.D_HTTP: + case ITaskWrapper.D_FTP: + case ITaskWrapper.M3U8_LIVE: + case ITaskWrapper.M3U8_VOD: + updateInterval = AriaConfig.getInstance().getDConfig().getUpdateInterval(); + break; + case ITaskWrapper.D_FTP_DIR: + case ITaskWrapper.DG_HTTP: + updateInterval = AriaConfig.getInstance().getDGConfig().getUpdateInterval(); + break; + case ITaskWrapper.U_HTTP: + case ITaskWrapper.U_FTP: + updateInterval = AriaConfig.getInstance().getUConfig().getUpdateInterval(); + } + return updateInterval; + } } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index bab6fab0..e7e8705b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -45,7 +45,7 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { protected final String TAG = CommonUtil.getClassName(getClass()); private long mCurrentLocation = 0; - protected IDGroupListener mListener; + private IDGroupListener mListener; private ScheduledThreadPoolExecutor mTimer; private long mUpdateInterval; private boolean isStop = false, isCancel = false; @@ -76,6 +76,10 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { */ protected abstract AbsSubDLoadUtil createSubLoader(DTaskWrapper wrapper, boolean needGetFileInfo); + protected IDGroupListener getListener() { + return mListener; + } + protected DGTaskWrapper getWrapper() { return mGTWrapper; } @@ -113,7 +117,7 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { getWrapper().setState(IEntity.STATE_POST_PRE); } mState.updateProgress(mCurrentLocation); - mScheduler = new Handler(looper, SimpleSchedulers.newInstance(mState)); + mScheduler = new Handler(looper, SimpleSchedulers.newInstance(mState, mGTWrapper.getKey())); } @Override public String getKey() { @@ -248,18 +252,26 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { mListener.onComplete(); return; } - - mListener.onPostPre(mGTWrapper.getEntity().getFileSize()); - if (mCurrentLocation > 0) { - mListener.onResume(mCurrentLocation); - } else { - mListener.onStart(mCurrentLocation); - } startTimer(); handlerTask(looper); Looper.loop(); } + /** + * 组合任务获取完成子任务的信息后调用 + */ + protected void onPostStart() { + if (isBreak()) { + return; + } + getListener().onPostPre(getWrapper().getEntity().getFileSize()); + if (getWrapper().getEntity().getFileSize() > 0) { + getListener().onResume(getWrapper().getEntity().getCurrentProgress()); + } else { + getListener().onStart(getWrapper().getEntity().getCurrentProgress()); + } + } + private synchronized void startTimer() { mState.isRunning = true; mTimer = new ScheduledThreadPoolExecutor(1); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java index 3fa50bb3..fe5ebb3b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java @@ -34,19 +34,19 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { protected SubLoader mDLoader; private DTaskWrapper mWrapper; private Handler mSchedulers; - private ChildDLoadListener mListener; private boolean needGetInfo; private boolean isStop = false, isCancel = false; + private String parentKey; /** * @param schedulers 调度器 * @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 */ - protected AbsSubDLoadUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo) { + protected AbsSubDLoadUtil(DTaskWrapper taskWrapper, Handler schedulers, boolean needGetInfo, String parentKey) { mWrapper = taskWrapper; mSchedulers = schedulers; + this.parentKey = parentKey; this.needGetInfo = needGetInfo; - mListener = new ChildDLoadListener(mSchedulers, AbsSubDLoadUtil.this); mDLoader = getLoader(); } @@ -57,6 +57,10 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { protected abstract LoaderStructure buildLoaderStructure(); + public String getParentKey() { + return parentKey; + } + protected boolean isNeedGetInfo() { return needGetInfo; } @@ -66,7 +70,7 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { } @Override public String getKey() { - return mWrapper.getKey(); + return mDLoader.getKey(); } public DTaskWrapper getWrapper() { @@ -77,15 +81,10 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { return mWrapper.getEntity(); } - public ChildDLoadListener getListener() { - return mListener; - } - @Override public void run() { if (isStop || isCancel) { return; } - mListener.onPre(); buildLoaderStructure(); mDLoader.run(); } @@ -107,10 +106,6 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { } } - public SubLoader getDownloader() { - return mDLoader; - } - /** * @deprecated 子任务不实现这个 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java deleted file mode 100644 index c606a78b..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/ChildDLoadListener.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.arialyy.aria.core.group; - -import android.os.Handler; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.listener.IDLoadListener; -import com.arialyy.aria.core.listener.ISchedulers; -import com.arialyy.aria.exception.BaseException; -import com.arialyy.aria.util.CommonUtil; - -/** - * 子任务事件监听 - */ -public class ChildDLoadListener implements IDLoadListener { - private DownloadEntity subEntity; - private int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度 - private long lastSaveTime; - private long lastLen; - private Handler schedulers; - private AbsSubDLoadUtil loader; - - ChildDLoadListener(Handler schedulers, AbsSubDLoadUtil loader) { - this.loader = loader; - this.schedulers = schedulers; - subEntity = loader.getEntity(); - subEntity.setFailNum(0); - lastLen = subEntity.getCurrentProgress(); - lastSaveTime = System.currentTimeMillis(); - } - - @Override public void supportBreakpoint(boolean support) { - - } - - @Override public void onPre() { - saveData(IEntity.STATE_PRE, subEntity.getCurrentProgress()); - } - - @Override public void onPostPre(long fileSize) { - subEntity.setFileSize(fileSize); - subEntity.setConvertFileSize(CommonUtil.formatFileSize(fileSize)); - saveData(IEntity.STATE_POST_PRE, subEntity.getCurrentProgress()); - sendToTarget(ISchedulers.POST_PRE, loader); - } - - @Override public void onResume(long resumeLocation) { - lastLen = resumeLocation; - saveData(IEntity.STATE_POST_PRE, subEntity.getCurrentProgress()); - sendToTarget(ISchedulers.START, loader); - } - - @Override public void onStart(long startLocation) { - lastLen = startLocation; - saveData(IEntity.STATE_POST_PRE, subEntity.getCurrentProgress()); - sendToTarget(ISchedulers.START, loader); - } - - @Override public void onProgress(long currentLocation) { - long diff = currentLocation - lastLen; - //mCurrentLocation += speed; - subEntity.setCurrentProgress(currentLocation); - handleSpeed(diff); - sendToTarget(ISchedulers.RUNNING, loader); - if (System.currentTimeMillis() - lastSaveTime >= RUN_SAVE_INTERVAL) { - saveData(IEntity.STATE_RUNNING, currentLocation); - lastSaveTime = System.currentTimeMillis(); - } - lastLen = currentLocation; - } - - @Override public void onStop(long stopLocation) { - handleSpeed(0); - saveData(IEntity.STATE_STOP, stopLocation); - sendToTarget(ISchedulers.STOP, loader); - } - - /** - * 组合任务子任务不允许删除 - */ - @Deprecated - @Override public void onCancel() { - - } - - @Override public void onComplete() { - subEntity.setComplete(true); - saveData(IEntity.STATE_COMPLETE, subEntity.getFileSize()); - handleSpeed(0); - sendToTarget(ISchedulers.COMPLETE, loader); - } - - @Override public void onFail(boolean needRetry, BaseException e) { - subEntity.setFailNum(subEntity.getFailNum() + 1); - saveData(IEntity.STATE_FAIL, subEntity.getCurrentProgress()); - handleSpeed(0); - sendToTarget(ISchedulers.FAIL, loader); - } - - private void handleSpeed(long speed) { - subEntity.setSpeed(speed); - subEntity.setConvertSpeed( - speed <= 0 ? "" : String.format("%s/s", CommonUtil.formatFileSize(speed))); - subEntity.setPercent((int) (subEntity.getFileSize() <= 0 ? 0 - : subEntity.getCurrentProgress() * 100 / subEntity.getFileSize())); - } - - private void saveData(int state, long location) { - loader.getWrapper().setState(state); - subEntity.setState(state); - subEntity.setComplete(state == IEntity.STATE_COMPLETE); - if (state == IEntity.STATE_CANCEL) { - subEntity.deleteData(); - } else if (state == IEntity.STATE_STOP) { - subEntity.setStopTime(System.currentTimeMillis()); - } else if (subEntity.isComplete()) { - subEntity.setCompleteTime(System.currentTimeMillis()); - subEntity.setCurrentProgress(subEntity.getFileSize()); - } else if (location > 0) { - subEntity.setCurrentProgress(location); - } - } - - /** - * 发送状态到子任务调度器{@link SimpleSchedulers},让调度器处理任务调度 - * - * @param state {@link ISchedulers} - */ - private void sendToTarget(int state, AbsSubDLoadUtil util) { - schedulers.obtainMessage(state, util).sendToTarget(); - } -} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java index 945e5b87..9634b497 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.group; +import android.os.Bundle; import android.os.Handler; import android.os.Message; import com.arialyy.aria.core.AriaConfig; @@ -37,45 +38,56 @@ class SimpleSchedulers implements Handler.Callback { private static final String TAG = "SimpleSchedulers"; private SimpleSubQueue mQueue; private GroupRunState mGState; + private String mKey; // 组合任务的key - private SimpleSchedulers(GroupRunState state) { + private SimpleSchedulers(GroupRunState state, String key) { mQueue = state.queue; mGState = state; + mKey = key; } - static SimpleSchedulers newInstance(GroupRunState state) { - - return new SimpleSchedulers(state); + static SimpleSchedulers newInstance(GroupRunState state, String key) { + return new SimpleSchedulers(state, key); } @Override public boolean handleMessage(Message msg) { - // todo key 应该从bundle中获取 - String key = (String) msg.obj; - AbsSubDLoadUtil loader = mQueue.getLoaderUtil(key); - if (loader == null) { - ALog.e(TAG, "子任务loder不存在,key:" + key); + Bundle b = msg.getData(); + if (b == null) { + ALog.w(TAG, "组合任务子任务调度数据为空"); + return true; + } + String threadName = b.getString(IThreadStateManager.DATA_THREAD_NAME); + AbsSubDLoadUtil loaderUtil = mQueue.getLoaderUtil(threadName); + if (loaderUtil == null) { + ALog.e(TAG, String.format("子任务loader不存在,state:%s,key:%s", msg.what, threadName)); return true; } - // todo 处理的是子任务的线程,需要删除 ThreadTaskManager.removeSingleTaskThread 删除线程任务 + long curLocation = b.getLong(IThreadStateManager.DATA_THREAD_LOCATION, + loaderUtil.getLoader().getWrapper().getEntity().getCurrentProgress()); + // 处理状态 switch (msg.what) { case IThreadStateManager.STATE_RUNNING: - mGState.listener.onSubRunning(loader.getEntity()); + long range = (long) msg.obj; + mGState.listener.onSubRunning(loaderUtil.getEntity(), range); break; - case PRE: - mGState.listener.onSubPre(loader.getEntity()); - mGState.updateCount(loader.getKey()); + case IThreadStateManager.STATE_PRE: + mGState.listener.onSubPre(loaderUtil.getEntity()); + mGState.updateCount(loaderUtil.getKey()); break; - case START: - mGState.listener.onSubStart(loader.getEntity()); + case IThreadStateManager.STATE_START: + mGState.listener.onSubStart(loaderUtil.getEntity()); break; case IThreadStateManager.STATE_STOP: - handleStop(loader); + handleStop(loaderUtil, curLocation); + ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); break; case IThreadStateManager.STATE_COMPLETE: - handleComplete(loader); + handleComplete(loaderUtil); + ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); break; case IThreadStateManager.STATE_FAIL: - handleFail(loader); + handleFail(loaderUtil); + ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); break; } return true; @@ -96,7 +108,7 @@ class SimpleSchedulers implements Handler.Callback { final int reTryNum = num; if ((!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) - || loaderUtil.getDownloader() == null // 如果获取不到文件信息,loader为空 + || loaderUtil.getLoader() == null // 如果获取不到文件信息,loader为空 || loaderUtil.getEntity().getFailNum() > reTryNum) { mQueue.removeTaskFromExecQ(loaderUtil); mGState.listener.onSubFail(loaderUtil.getEntity(), new TaskException(TAG, @@ -136,8 +148,8 @@ class SimpleSchedulers implements Handler.Callback { * 1、所有的子任务已经停止,则认为组合任务停止 * 2、completeNum + failNum + stopNum = subSize,则认为组合任务停止 */ - private synchronized void handleStop(AbsSubDLoadUtil loadUtil) { - mGState.listener.onSubStop(loadUtil.getEntity()); + private synchronized void handleStop(AbsSubDLoadUtil loadUtil, long curLocation) { + mGState.listener.onSubStop(loadUtil.getEntity(), curLocation); mGState.countStopNum(loadUtil.getKey()); if (mGState.getStopNum() == mGState.getSubSize() || mGState.getStopNum() diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java index 3ca54e71..57020f3c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.group; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CommonUtil; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -29,7 +30,7 @@ import java.util.Set; * 组合任务队列,该队列生命周期和{@link AbsGroupLoaderUtil}生命周期一致 */ class SimpleSubQueue implements ISubQueue { - private static final String TAG = "SimpleSubQueue"; + private final String TAG = CommonUtil.getClassName(getClass()); /** * 缓存下载器 */ @@ -162,12 +163,7 @@ class SimpleSubQueue implements ISubQueue { } @Override public void removeTaskFromExecQ(AbsSubDLoadUtil fileer) { - if (mExec.containsKey(fileer.getKey())) { - if (fileer.isRunning()) { - fileer.stop(); - } - mExec.remove(fileer.getKey()); - } + mExec.remove(fileer.getKey()); } @Override public void removeTask(AbsSubDLoadUtil fileer) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java index 66c9f5fc..5b3811a7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java @@ -32,8 +32,10 @@ public interface IThreadStateManager extends ILoaderComponent { int STATE_UPDATE_PROGRESS = 0x06; int STATE_PRE = 0x07; int STATE_START = 0x08; - String KEY_RETRY = "KEY_RETRY"; - String KEY_ERROR_INFO = "KEY_ERROR_INFO"; + String DATA_RETRY = "DATA_RETRY"; + String DATA_ERROR_INFO = "DATA_ERROR_INFO"; + String DATA_THREAD_NAME = "DATA_THREAD_NAME"; + String DATA_THREAD_LOCATION = "DATA_THREAD_LOCATION"; /** * 任务是否已经失败 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java index f15167c2..5c21a326 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java @@ -21,8 +21,8 @@ import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.group.GroupSendParams; import com.arialyy.aria.core.inf.IEntity; -import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.inf.TaskSchedulerType; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.exception.BaseException; @@ -47,6 +47,7 @@ public class DownloadGroupListener @Override public void onSubPre(DownloadEntity subEntity) { + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_PRE, subEntity); sendInState2Target(ISchedulers.SUB_PRE, subEntity); } @@ -58,12 +59,15 @@ public class DownloadGroupListener @Override public void onSubStart(DownloadEntity subEntity) { + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_RUNNING, subEntity); sendInState2Target(ISchedulers.SUB_START, subEntity); } @Override - public void onSubStop(DownloadEntity subEntity) { + public void onSubStop(DownloadEntity subEntity, long stopLocation) { + subEntity.setCurrentProgress(stopLocation); + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_STOP, subEntity); saveCurrentLocation(); sendInState2Target(ISchedulers.SUB_STOP, subEntity); @@ -71,6 +75,7 @@ public class DownloadGroupListener @Override public void onSubComplete(DownloadEntity subEntity) { + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_COMPLETE, subEntity); saveCurrentLocation(); sendInState2Target(ISchedulers.SUB_COMPLETE, subEntity); @@ -78,6 +83,7 @@ public class DownloadGroupListener @Override public void onSubFail(DownloadEntity subEntity, BaseException e) { + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_FAIL, subEntity); saveCurrentLocation(); sendInState2Target(ISchedulers.SUB_FAIL, subEntity); @@ -89,13 +95,16 @@ public class DownloadGroupListener @Override public void onSubCancel(DownloadEntity subEntity) { + handleSpeed(subEntity, 0); saveSubState(IEntity.STATE_CANCEL, subEntity); saveCurrentLocation(); sendInState2Target(ISchedulers.SUB_CANCEL, subEntity); } @Override - public void onSubRunning(DownloadEntity subEntity) { + public void onSubRunning(DownloadEntity subEntity, long currentProgress) { + + handleSpeed(subEntity, currentProgress); if (System.currentTimeMillis() - mLastSaveTime >= RUN_SAVE_INTERVAL) { saveSubState(IEntity.STATE_RUNNING, subEntity); mLastSaveTime = System.currentTimeMillis(); @@ -103,6 +112,21 @@ public class DownloadGroupListener sendInState2Target(ISchedulers.SUB_RUNNING, subEntity); } + private void handleSpeed(DownloadEntity subEntity, long currentProgress) { + if (currentProgress == 0){ + subEntity.setSpeed(0); + subEntity.setConvertSpeed("0kb/s"); + return; + } + long range = currentProgress - subEntity.getCurrentProgress() ; + subEntity.setSpeed(range); + subEntity.setConvertSpeed( + range <= 0 ? "" : String.format("%s/s", CommonUtil.formatFileSize(range))); + subEntity.setPercent((int) (subEntity.getFileSize() <= 0 ? 0 + : subEntity.getCurrentProgress() * 100 / subEntity.getFileSize())); + subEntity.setCurrentProgress(currentProgress); + } + /** * 将任务状态发送给下载器 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java index eca54860..31fc5ddc 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IDGroupListener.java @@ -16,7 +16,6 @@ package com.arialyy.aria.core.listener; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.exception.BaseException; /** @@ -45,7 +44,7 @@ public interface IDGroupListener extends IDLoadListener { /** * 子任务停止下载 */ - void onSubStop(DownloadEntity subEntity); + void onSubStop(DownloadEntity subEntity, long stopLocation); /** * 子任务下载完成 @@ -64,6 +63,8 @@ public interface IDGroupListener extends IDLoadListener { /** * 子任务执行中 + * + * @param currentProgress 当前进度 */ - void onSubRunning(DownloadEntity subEntity); + void onSubRunning(DownloadEntity subEntity, long currentProgress); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java index d775f3e2..c7c8db5b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java @@ -84,6 +84,8 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder { config.taskWrapper = mWrapper; config.record = record; config.stateHandler = mStateHandler; + config.threadType = SubThreadConfig.getThreadType(mWrapper.getRequestType()); + config.updateInterval = SubThreadConfig.getUpdateInterval(mWrapper.getRequestType()); return createThreadTask(config); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java index 72e6430f..bff67ba7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java @@ -90,8 +90,8 @@ public class NormalThreadStateManager implements IThreadStateManager { mFailNum++; if (isFail()) { Bundle b = msg.getData(); - mListener.onFail(b.getBoolean(KEY_RETRY, false), - (BaseException) b.getSerializable(KEY_ERROR_INFO)); + mListener.onFail(b.getBoolean(DATA_RETRY, false), + (BaseException) b.getSerializable(DATA_ERROR_INFO)); quitLooper(); } break; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java index d5eb3d7d..8a5f0379 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java @@ -15,13 +15,17 @@ */ package com.arialyy.aria.core.loader; +import android.os.Bundle; import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; +import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.inf.IThreadStateManager; -import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.task.IThreadTask; +import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; @@ -42,29 +46,84 @@ public final class SubLoader implements ILoader, ILoaderVisitor { private IThreadTaskBuilder ttBuild; private IRecordHandler recordHandler; private IThreadTask threadTask; + private String parentKey; public SubLoader(AbsTaskWrapper wrapper, Handler schedulers) { this.wrapper = wrapper; this.schedulers = schedulers; } + public AbsTaskWrapper getWrapper() { + return wrapper; + } + + /** + * 发送状态到调度器 + * + * @param state {@link IThreadStateManager} + */ + private void sendNormalState(int state) { + Message msg = schedulers.obtainMessage(); + Bundle b = msg.getData(); + if (b == null) { + b = new Bundle(); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, getKey()); + msg.what = state; + msg.setData(b); + msg.sendToTarget(); + } + + /** + * 发送失败的状态 + */ + private void sendFailState(boolean needRetry) { + Message msg = schedulers.obtainMessage(); + Bundle b = msg.getData(); + if (b == null) { + b = new Bundle(); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, getKey()); + b.putBoolean(IThreadStateManager.DATA_RETRY, needRetry); + msg.what = IThreadStateManager.STATE_FAIL; + msg.setData(b); + msg.sendToTarget(); + } + private void handlerTask() { - List task = - ttBuild.buildThreadTask(recordHandler.getRecord(wrapper.getEntity().getFileSize()), - schedulers); + TaskRecord record = recordHandler.getRecord(wrapper.getEntity().getFileSize()); + if (record.threadRecords != null + && !record.threadRecords.isEmpty() + && record.threadRecords.get(0).isComplete) { + ALog.d(TAG, "子任务已完成,key:" + wrapper.getKey()); + sendNormalState(IThreadStateManager.STATE_COMPLETE); + return; + } + List task = ttBuild.buildThreadTask(record, schedulers); if (task == null || task.isEmpty()) { - ALog.e(TAG, "创建子任务的线程任务失败,key:" + getKey()); - //schedulers.obtainMessage(ISchedulers.FAIL, SubLoader.this).sendToTarget(); + ALog.e(TAG, "创建子任务的线程任务失败,key:" + wrapper.getKey()); + sendFailState(false); + return; + } + if (TextUtils.isEmpty(parentKey)) { + ALog.e(TAG, "parentKey为空"); + sendFailState(false); return; } + sendNormalState(IThreadStateManager.STATE_PRE); threadTask = task.get(0); try { - ThreadTaskManager.getInstance().startThread(getKey(), threadTask); + ThreadTaskManager.getInstance().startThread(parentKey, threadTask); + sendNormalState(IThreadStateManager.STATE_START); } catch (Exception e) { e.printStackTrace(); } } + public void setParentKey(String parentKey) { + this.parentKey = parentKey; + } + public void setNeedGetInfo(boolean needGetInfo) { this.needGetInfo = needGetInfo; } @@ -91,7 +150,7 @@ public final class SubLoader implements ILoader, ILoaderVisitor { } @Override public boolean isRunning() { - return !threadTask.isBreak(); + return threadTask != null && !threadTask.isBreak(); } @Override public void cancel() { @@ -112,8 +171,13 @@ public final class SubLoader implements ILoader, ILoaderVisitor { return false; } + /** + * 线程名,一个子任务的loader只有一个线程,使用线程名标示key + * + * @return {@link ThreadTask#getThreadName()} + */ @Override public String getKey() { - return wrapper.getKey(); + return CommonUtil.getThreadName(wrapper.getKey(), 0); } /** @@ -136,7 +200,7 @@ public final class SubLoader implements ILoader, ILoaderVisitor { } @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { - schedulers.obtainMessage(ISchedulers.FAIL, SubLoader.this).sendToTarget(); + sendFailState(needRetry); } }); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java index 39f8233e..b985dca8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.manager; +import android.text.TextUtils; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; @@ -24,7 +25,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -153,6 +153,49 @@ public class ThreadTaskManager { } } + /** + * 根据线程名删除任务的中的线程 + * + * @param key 任务的key,如果是组合任务,则为组合任务的key + * @param threadName 线程名 + * @return true 删除线程成功;false 删除线程失败 + */ + public boolean removeSingleTaskThread(String key, String threadName) { + try { + LOCK.tryLock(2, TimeUnit.SECONDS); + if (mExePool.isShutdown()) { + ALog.e(TAG, "线程池已经关闭"); + return false; + } + if (TextUtils.isEmpty(threadName)) { + ALog.e(TAG, "线程名为空"); + return false; + } + + key = getKey(key); + Set temp = mThreadTasks.get(key); + if (temp != null && temp.size() > 0) { + FutureContainer tempC = null; + for (FutureContainer container : temp) { + if (container.threadTask.getThreadName().equals(threadName)) { + tempC = container; + break; + } + } + if (tempC != null) { + tempC.threadTask.destroy(); + temp.remove(tempC); + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + LOCK.unlock(); + } + return false; + } + /** * 删除单个线程任务 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java index 364510a6..1077d357 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTask.java @@ -90,7 +90,11 @@ public interface IThreadTask extends Callable { /** * 获取线程id - * @return */ int getThreadId(); + + /** + * 线程名字,命名规则:md5(任务地址 + 线程id) + */ + String getThreadName(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index e8cab93c..b34abaf0 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -53,7 +53,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private IEntity mEntity; protected AbsTaskWrapper mTaskWrapper; private int mFailTimes = 0; - private long mLastSaveTime; + private long mLastSaveTime, mLastSendProgressTime; private boolean isNotNetRetry; //断网情况是否重试 private boolean taskBreak = false; //任务跳出 private boolean isDestroy = false; @@ -67,6 +67,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private long mRangeProgress; private IThreadTaskAdapter mAdapter; private ThreadRecord mRecord; + private String mThreadNmae; + private long updateInterval = 1000; // 更新间隔 private Thread mConfigThread = new Thread(new Runnable() { @Override public void run() { @@ -87,6 +89,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { isNotNetRetry = AriaConfig.getInstance().getAConfig().isNotNetRetry(); mRangeProgress = mRecord.startLocation; + updateInterval = config.updateInterval; } /** @@ -219,7 +222,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { blockFile.getName(), blockFile.length(), mRecord.blockLen, mRecord.startLocation, mRecord.endLocation)); if (blockFile.exists()) { - blockFile.delete(); + FileUtil.deleteFile(blockFile); ALog.i(TAG, String.format("删除分块【%s】成功", blockFile.getName())); } retryBlockTask(isBreak()); @@ -232,19 +235,24 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { return mRecord.threadId; } + @Override public String getThreadName() { + return mThreadNmae == null ? CommonUtil.getThreadName(getConfig().url, getThreadId()) + : mThreadNmae; + } + /** * 停止任务 */ @Override public void stop() { isStop = true; + final long stopLocation = mRangeProgress; updateState(IThreadStateManager.STATE_STOP, null); if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { writeConfig(false, getConfig().tempFile.length()); ALog.i(TAG, String.format("任务【%s】已停止", getFileName())); } else { if (mTaskWrapper.isSupportBP()) { - final long stopLocation = mRangeProgress; ALog.d(TAG, String.format("任务【%s】thread__%s__停止【当前线程停止位置:%s】", getFileName(), mRecord.threadId, stopLocation)); @@ -264,12 +272,14 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { @Override public synchronized void updateState(int state, Bundle bundle) { Message msg = mStateHandler.obtainMessage(); - - msg.what = state; - if (bundle != null) { - msg.setData(bundle); + if (bundle == null) { + bundle = new Bundle(); } - int reqType = mTaskWrapper.getRequestType(); + msg.setData(bundle); + bundle.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); + bundle.putLong(IThreadStateManager.DATA_THREAD_LOCATION, mRangeProgress); + msg.what = state; + int reqType = getConfig().threadType; if (reqType == ITaskWrapper.M3U8_VOD || reqType == ITaskWrapper.M3U8_LIVE) { sendM3U8Info(state, msg); } @@ -287,9 +297,6 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } Bundle bundle = msg.getData(); if ((state == IThreadStateManager.STATE_COMPLETE || state == IThreadStateManager.STATE_FAIL)) { - if (bundle == null) { - bundle = new Bundle(); - } bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url); bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath()); bundle.putInt(ISchedulers.DATA_M3U8_PEER_INDEX, getConfig().peerIndex); @@ -318,7 +325,21 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { if (!loopThread.isAlive() || loopThread.isInterrupted()) { return; } - mStateHandler.obtainMessage(IThreadStateManager.STATE_RUNNING, len).sendToTarget(); + // 不需要太频繁发送,以减少消息队列的压力 + if (System.currentTimeMillis() - mLastSendProgressTime > updateInterval) { + Message msg = mStateHandler.obtainMessage(); + Bundle b = msg.getData(); + if (b == null) { + b = new Bundle(); + msg.setData(b); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); + msg.what = IThreadStateManager.STATE_RUNNING; + msg.obj = mRangeProgress; + msg.sendToTarget(); + mLastSendProgressTime = System.currentTimeMillis(); + } + if (System.currentTimeMillis() - mLastSaveTime > 5000 && mRangeProgress < mRecord.endLocation) { mLastSaveTime = System.currentTimeMillis(); @@ -435,7 +456,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { */ if (blockFileLen > threadRect) { ALog.i(TAG, String.format("分块【%s】错误,将重新下载该分块", temp.getName())); - temp.delete(); + FileUtil.deleteFile(temp); mRecord.startLocation = mRecord.endLocation - mRecord.blockLen; mRecord.isComplete = false; } else if (blockFileLen < mRecord.blockLen) { @@ -459,9 +480,9 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { */ private void sendFailMsg(BaseException e, boolean needRetry) { Bundle b = new Bundle(); - b.putBoolean(IThreadStateManager.KEY_RETRY, needRetry); + b.putBoolean(IThreadStateManager.DATA_RETRY, needRetry); if (e != null) { - b.putSerializable(IThreadStateManager.KEY_ERROR_INFO, e); + b.putSerializable(IThreadStateManager.DATA_ERROR_INFO, e); updateState(IThreadStateManager.STATE_FAIL, b); } else { updateState(IThreadStateManager.STATE_FAIL, b); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java index 7f3acdc8..e222842d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/wrapper/ITaskWrapper.java @@ -97,7 +97,10 @@ public interface ITaskWrapper { IEntity getEntity(); /** - * 获取任务唯一标志 + * 获取任务唯一标志: + * 下载任务,对应文件的下载地址 + * 上传任务,对应需要上传的文件路径 + * 组合任务,对应组合任务的groupHash */ String getKey(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index c046381f..bbb0f4d6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -61,6 +61,16 @@ public class CommonUtil { public static final String SERVER_CHARSET = "ISO-8859-1"; private static long lastClickTime; + /** + * 获取线程名称,命名规则:md5(任务地址 + 线程id) + * + * @param url 任务地址 + * @param threadId 线程id + */ + public static String getThreadName(String url, int threadId) { + return getStrMd5(url.concat(String.valueOf(threadId))); + } + /** * 检查sql的expression是否合法 * @@ -436,7 +446,7 @@ public class CommonUtil { md.update(str.getBytes()); return new BigInteger(1, md.digest()).toString(16); } catch (NoSuchAlgorithmException e) { - ALog.e(TAG, e.getMessage()); + e.printStackTrace(); } return ""; } @@ -633,7 +643,7 @@ public class CommonUtil { ignore.add(field); } } - if (!ignore.isEmpty()){ + if (!ignore.isEmpty()) { fields.removeAll(ignore); } return fields; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 45b71aec..02d51649 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -85,7 +85,7 @@ public class RecordUtil { DbEntity.findRelationData(RecordWrapper.class, "dGroupHash=?", groupEntity.getGroupHash()); if (records == null || records.isEmpty()) { - ALog.w(TAG, "组任务记录删除失败,记录为null"); + ALog.w(TAG, "组任务记录已删除"); } else { for (RecordWrapper record : records) { if (record == null || record.taskRecord == null) { diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 2b9980a1..48d9d47b 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -67,6 +67,7 @@ public class DownloadGroupActivity extends BaseActivity implements ISchedulers { - private final String TAG = "AbsSchedulers"; + private final String TAG = CommonUtil.getClassName(getClass()); private static volatile TaskSchedulers INSTANCE; private static FailureTaskHandler mFailureTaskHandler; diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java index 24d9236f..29fd34ac 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/HttpFileInfoTask.java @@ -85,7 +85,7 @@ public final class HttpFileInfoTask implements IInfoTask, Runnable { } catch (IOException e) { e.printStackTrace(); failDownload(new AriaIOException(TAG, - String.format("下载失败,filePath: %s, url: %s", mEntity.getDownloadPath(), mEntity.getUrl())), + String.format("下载失败,filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl())), true); } finally { if (conn != null) { @@ -131,7 +131,7 @@ public final class HttpFileInfoTask implements IInfoTask, Runnable { if (!FileUtil.checkMemorySpace(mEntity.getFilePath(), len)) { failDownload(new TaskException(TAG, - String.format("下载失败,内存空间不足;filePath: %s, url: %s", mEntity.getDownloadPath(), + String.format("下载失败,内存空间不足;filePath: %s, url: %s", mEntity.getFilePath(), mEntity.getUrl())), false); return; } diff --git a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java index e93e9f1e..32f1308b 100644 --- a/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java +++ b/HttpComponent/src/main/java/com/arialyy/aria/http/download/HttpDGInfoTask.java @@ -132,7 +132,7 @@ public final class HttpDGInfoTask implements IInfoTask { wrapper.getEntity().setFileSize(size); wrapper.getEntity().update(); getLenComplete = true; - ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s,失败的只任务数:%s", size, failCount)); + ALog.d(TAG, String.format("获取组合任务长度完成,组合任务总长度:%s,失败的子任务数:%s", size, failCount)); callback.onSucceed(wrapper.getKey(), new CompleteInfo()); notifyLock(); } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java index 62b27892..ab7901c5 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java @@ -144,6 +144,10 @@ final class LiveStateManager implements IThreadStateManager { return mProgress; } + @Override public void updateCurrentProgress(long currentProgress) { + mProgress = currentProgress; + } + @Override public void setLooper(TaskRecord taskRecord, Looper looper) { mLooper = looper; } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java index cbba8db3..2b880862 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java @@ -247,6 +247,10 @@ public final class VodStateManager implements IThreadStateManager { return progress; } + @Override public void updateCurrentProgress(long currentProgress) { + progress = currentProgress; + } + private void printInfo(String tag) { if (false) { ALog.d(tag, String.format( diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 99de4cc3..9fe5c43a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -21,6 +21,7 @@ import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.BufferedRandomAccessFile; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.FileUtil; import java.io.File; import java.io.IOException; @@ -32,7 +33,7 @@ import java.io.IOException; * @Date 2019-09-19 */ public class RecordHelper { - private String TAG = "RecordHelper"; + private String TAG = CommonUtil.getClassName(getClass()); private AbsTaskWrapper mWrapper; protected TaskRecord mTaskRecord; @@ -166,6 +167,7 @@ public class RecordHelper { tr.isComplete = false; tr.endLocation = mWrapper.getEntity().getFileSize(); } else if (file.length() == mWrapper.getEntity().getFileSize()) { + ALog.d(TAG, "文件长度一致,线程完成"); tr.isComplete = true; } else { if (file.length() != tr.startLocation) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java index 49fcbba8..f4dfbd84 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/config/XMLReader.java @@ -27,7 +27,7 @@ import org.xml.sax.helpers.DefaultHandler; * Created by lyy on 2017/5/22. 读取配置文件 */ public class XMLReader extends DefaultHandler { - private final String TAG = "XMLReader"; + private final String TAG = CommonUtil.getClassName(this); private DownloadConfig mDownloadConfig = Configuration.getInstance().downloadCfg; private UploadConfig mUploadConfig = Configuration.getInstance().uploadCfg; @@ -62,13 +62,13 @@ public class XMLReader extends DefaultHandler { String value = attributes.getValue("value"); switch (qName) { - case "getCreatedThreadNum": // 线程数 + case "threadNum": // 线程数 int threadNum = checkInt(value) ? Integer.parseInt(value) : 3; if (threadNum < 1) { ALog.w(TAG, "下载线程数不能小于 1"); threadNum = 1; } - setField("getCreatedThreadNum", threadNum, ConfigType.DOWNLOAD); + setField("threadNum", threadNum, ConfigType.DOWNLOAD); break; case "maxTaskNum": //最大任务书 int maxTaskNum = checkInt(value) ? Integer.parseInt(value) : 2; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java index b6264596..19d67809 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadEntity.java @@ -130,27 +130,11 @@ public class DownloadEntity extends AbsNormalEntity implements Parcelable { this.groupHash = groupHash; } - /** - * 后面会删除该方法,请使用{@link #getFilePath()} - */ - @Deprecated - public String getDownloadPath() { - return downloadPath; - } - @Override public String getFilePath() { return downloadPath; } - /** - * 后面会删除该方法,请使用{@link #setFilePath(String)} - */ - @Deprecated - public DownloadEntity setDownloadPath(String downloadPath) { - return setFilePath(downloadPath); - } - public DownloadEntity setFilePath(String filePath) { this.downloadPath = filePath; return this; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index e7e8705b..7bd3ff25 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -98,14 +98,14 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { private void initState(Looper looper) { mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue); for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { - File subFile = new File(wrapper.getEntity().getFilePath()); + long fileLen = checkFileExists(wrapper.getEntity().getFilePath()); if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE - && subFile.exists() - && subFile.length() == wrapper.getEntity().getFileSize()) { + && fileLen != -1 + && fileLen == wrapper.getEntity().getFileSize()) { mState.updateCompleteNum(); mCurrentLocation += wrapper.getEntity().getFileSize(); } else { - if (!subFile.exists()) { + if (fileLen == -1) { wrapper.getEntity().setCurrentProgress(0); } wrapper.getEntity().setState(IEntity.STATE_POST_PRE); @@ -120,6 +120,25 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { mScheduler = new Handler(looper, SimpleSchedulers.newInstance(mState, mGTWrapper.getKey())); } + /** + * 检查文件是否存在,需要检查普通任务和分块任务的 + * + * @param filePath 文件路径 + * @return 文件存在返回文件长度,不存在返回-1 + */ + private long checkFileExists(String filePath) { + File temp = new File(filePath); + if (temp.exists()) { + return temp.length(); + } + File block = new File(String.format(IRecordHandler.SUB_PATH, filePath, 0)); + if (block.exists()) { + return block.length(); + } else { + return -1; + } + } + @Override public String getKey() { return mGTWrapper.getKey(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java index 5b3811a7..01be3a84 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IThreadStateManager.java @@ -36,6 +36,7 @@ public interface IThreadStateManager extends ILoaderComponent { String DATA_ERROR_INFO = "DATA_ERROR_INFO"; String DATA_THREAD_NAME = "DATA_THREAD_NAME"; String DATA_THREAD_LOCATION = "DATA_THREAD_LOCATION"; + String DATA_ADD_LEN = "DATA_ADD_LEN"; // 增加的长度 /** * 任务是否已经失败 @@ -58,6 +59,12 @@ public interface IThreadStateManager extends ILoaderComponent { */ long getCurrentProgress(); + /** + * 更新当前进度 + * @param currentProgress 当前进度 + */ + void updateCurrentProgress(long currentProgress); + /** * 设置消息循环体 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index 2dc40426..6f370cf2 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -136,7 +136,6 @@ public abstract class BaseListener= RUN_SAVE_INTERVAL) { saveSubState(IEntity.STATE_RUNNING, subEntity); mLastSaveTime = System.currentTimeMillis(); @@ -112,7 +112,7 @@ public class DownloadGroupListener sendInState2Target(ISchedulers.SUB_RUNNING, subEntity); } - private void handleSpeed(DownloadEntity subEntity, long currentProgress) { + private void handleSubSpeed(DownloadEntity subEntity, long currentProgress) { if (currentProgress == 0){ subEntity.setSpeed(0); subEntity.setConvertSpeed("0kb/s"); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java index c7c8db5b..33d7eb86 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java @@ -152,7 +152,7 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder { } threadTasks.add(task); } - if (currentProgress != 0 && currentProgress != getEntity().getCurrentProgress()) { + if (currentProgress != getEntity().getCurrentProgress()) { ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); getEntity().setCurrentProgress(currentProgress); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java index c23c7385..7f88fc9c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/IRecordHandler.java @@ -37,7 +37,7 @@ public interface IRecordHandler extends ILoaderComponent { long SUB_LEN = 1024 * 1024; /** - * 分块文件路径 + * 分块文件路径,文件路径.线程id.part */ String SUB_PATH = "%s.%s.part"; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index aeda85c5..255beabc 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -28,6 +28,7 @@ import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; +import com.arialyy.aria.util.FileUtil; import java.io.File; /** @@ -71,17 +72,6 @@ public class NormalLoader extends AbsNormalLoader { EventMsgUtil.getDefault().unRegister(this); } - @Override protected void onPostPre() { - super.onPostPre(); - if (getListener() instanceof IDLoadListener) { - ((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize()); - } - File file = new File(getEntity().getFilePath()); - if (file.getParentFile() != null && !file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } - } - ///** // * 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径 // */ @@ -105,11 +95,21 @@ public class NormalLoader extends AbsNormalLoader { } protected void startThreadTask() { + + if (getListener() instanceof IDLoadListener) { + ((IDLoadListener) getListener()).onPostPre(getEntity().getFileSize()); + } + File file = new File(getEntity().getFilePath()); + if (file.getParentFile() != null && !file.getParentFile().exists()) { + FileUtil.createDir(file.getPath()); + } mRecord = mRecordHandler.getRecord(getFileSize()); mStateManager.setLooper(mRecord, looper); getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, new Handler(looper, mStateManager.getHandlerCallback()))); startThreadNum = mTTBuilder.getCreatedThreadNum(); + + mStateManager.updateCurrentProgress(getEntity().getCurrentProgress()); if (mStateManager.getCurrentProgress() > 0) { getListener().onResume(mStateManager.getCurrentProgress()); } else { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java index bff67ba7..0e4e5744 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalThreadStateManager.java @@ -61,11 +61,11 @@ public class NormalThreadStateManager implements IThreadStateManager { mLooper = looper; } - private void checkLooper(){ - if (mTaskRecord == null){ + private void checkLooper() { + if (mTaskRecord == null) { throw new NullPointerException("任务记录为空"); } - if (mLooper == null){ + if (mLooper == null) { throw new NullPointerException("Looper为空"); } } @@ -112,9 +112,12 @@ public class NormalThreadStateManager implements IThreadStateManager { } break; case STATE_RUNNING: - if (msg.obj instanceof Long) { - mProgress += (long) msg.obj; + Bundle b = msg.getData(); + if (b != null) { + long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0); + mProgress += len; } + break; case STATE_UPDATE_PROGRESS: if (msg.obj == null) { @@ -128,11 +131,8 @@ public class NormalThreadStateManager implements IThreadStateManager { } }; - /** - * 不要使用handle更新启动线程的进度,因为有延迟 - */ - void updateProgress(long curProgress) { - mProgress = curProgress; + @Override public void updateCurrentProgress(long currentProgress) { + mProgress = currentProgress; } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java index 8a5f0379..5d162beb 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java @@ -30,6 +30,7 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; +import java.io.File; import java.util.List; /** @@ -93,6 +94,8 @@ public final class SubLoader implements ILoader, ILoaderVisitor { private void handlerTask() { TaskRecord record = recordHandler.getRecord(wrapper.getEntity().getFileSize()); if (record.threadRecords != null + && !TextUtils.isEmpty(record.filePath) + && new File(record.filePath).exists() && !record.threadRecords.isEmpty() && record.threadRecords.get(0).isComplete) { ALog.d(TAG, "子任务已完成,key:" + wrapper.getKey()); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index b34abaf0..8c36ae72 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -64,7 +64,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { /** * 当前线程的下去区间的进度 */ - private long mRangeProgress; + private long mRangeProgress, mLastRangeProgress; private IThreadTaskAdapter mAdapter; private ThreadRecord mRecord; private String mThreadNmae; @@ -89,6 +89,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { isNotNetRetry = AriaConfig.getInstance().getAConfig().isNotNetRetry(); mRangeProgress = mRecord.startLocation; + mLastRangeProgress = mRangeProgress; updateInterval = config.updateInterval; } @@ -304,7 +305,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } @Override public synchronized void updateCompleteState() { - ALog.i(TAG, String.format("任务【%s】线程__%s__下载完毕", getTaskWrapper().getKey(), mRecord.threadId)); + ALog.i(TAG, String.format("任务【%s】线程__%s__完成", getTaskWrapper().getKey(), mRecord.threadId)); writeConfig(true, mRecord.endLocation); updateState(IThreadStateManager.STATE_COMPLETE, null); } @@ -334,9 +335,11 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { msg.setData(b); } b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); + b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress); msg.what = IThreadStateManager.STATE_RUNNING; msg.obj = mRangeProgress; msg.sendToTarget(); + mLastRangeProgress = mRangeProgress; mLastSendProgressTime = System.currentTimeMillis(); } @@ -402,13 +405,13 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected( AriaConfig.getInstance().getAPP()) || isNotNetRetry) && !isBreak()) { - ALog.w(TAG, String.format("ts切片【%s】正在重试", getFileName())); + ALog.w(TAG, String.format("ts切片【%s】第%s重试", getFileName(), String.valueOf(mFailTimes))); mFailTimes++; FileUtil.deleteFile(mConfig.tempFile); FileUtil.createFile(mConfig.tempFile); ThreadTaskManager.getInstance().retryThread(this); } else { - sendFailMsg(null, needRetry); + sendFailMsg(null, false); } } @@ -425,12 +428,12 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } if (mFailTimes < RETRY_NUM && needRetry && (NetUtils.isConnected( AriaConfig.getInstance().getAPP()) || isNotNetRetry) && !isBreak()) { - ALog.w(TAG, String.format("分块【%s】正在重试", getFileName())); + ALog.w(TAG, String.format("分块【%s】第%s次重试", getFileName(), String.valueOf(mFailTimes))); mFailTimes++; handleBlockRecord(); ThreadTaskManager.getInstance().retryThread(this); } else { - sendFailMsg(null, needRetry); + sendFailMsg(null, false); } } @@ -493,7 +496,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { * 将记录写入到配置文件 * * @param isComplete 当前线程是否完成 {@code true}完成 - * @param record 当前下载进度 + * @param record 当前进度 */ private void writeConfig(boolean isComplete, final long record) { if (mRecord != null) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java index b5c8bd26..00861103 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java @@ -190,6 +190,27 @@ public class FileUtil { return false; } + /** + * 删除文件夹 + */ + public static boolean deleteDir(File dirFile) { + // 如果dir对应的文件不存在,则退出 + if (!dirFile.exists()) { + return false; + } + + if (dirFile.isFile()) { + return dirFile.delete(); + } else { + + for (File file : dirFile.listFiles()) { + deleteDir(file); + } + } + + return dirFile.delete(); + } + /** * 将对象写入文件 * @@ -275,9 +296,14 @@ public class FileUtil { FileOutputStream fos = null; FileChannel foc = null; try { + if (file.exists() && file.isDirectory()) { + ALog.w(TAG, String.format("路径【%s】是文件夹,将删除该文件夹", targetPath)); + FileUtil.deleteDir(file); + } if (!file.exists()) { - file.createNewFile(); + FileUtil.createFile(file); } + fos = new FileOutputStream(targetPath); foc = fos.getChannel(); List streams = new LinkedList<>(); @@ -383,8 +409,8 @@ public class FileUtil { */ public static boolean checkMemorySpace(String path, long fileSize) { File temp = new File(path); - if (!temp.exists()){ - if (!temp.getParentFile().exists()){ + if (!temp.exists()) { + if (!temp.getParentFile().exists()) { FileUtil.createDir(temp.getParentFile().getPath()); } path = temp.getParentFile().getPath(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 02d51649..3debb45f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -142,7 +142,7 @@ public class RecordUtil { int type; if (entity instanceof DownloadEntity) { type = IRecordHandler.TYPE_DOWNLOAD; - filePath = ((DownloadEntity) entity).getDownloadPath(); + filePath = entity.getFilePath(); } else if (entity instanceof UploadEntity) { type = IRecordHandler.TYPE_UPLOAD; filePath = entity.getFilePath(); diff --git a/app/src/main/java/com/arialyy/simple/DbTestActivity.java b/app/src/main/java/com/arialyy/simple/DbTestActivity.java index feb15785..8815192a 100644 --- a/app/src/main/java/com/arialyy/simple/DbTestActivity.java +++ b/app/src/main/java/com/arialyy/simple/DbTestActivity.java @@ -55,7 +55,7 @@ public class DbTestActivity extends BaseActivity { DownloadEntity entity = new DownloadEntity(); entity.setUrl(url); entity.setFileName("ssssssssssssssssss"); - entity.setDownloadPath(key); + entity.setFilePath(key); DTaskWrapper dte = new DTaskWrapper(entity); diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java index 800ec4e3..4b87c0a8 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java @@ -129,7 +129,7 @@ public class DownloadModule extends BaseModule { DownloadEntity entity = new DownloadEntity(); entity.setFileName(name); entity.setUrl(downloadUrl); - entity.setDownloadPath(path); + entity.setFilePath(path); return entity; } } \ No newline at end of file diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java index 6f8e28fa..c170446b 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadActivity.java @@ -28,6 +28,7 @@ import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.task.DownloadTask; +import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.T; @@ -48,7 +49,7 @@ public class FtpDownloadActivity extends BaseActivity " + CommonUtil.getFileMD5(new File(task.getFilePath()))); T.showShort(this, "文件:" + task.getEntity().getFileName() + ",下载完成"); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java index ec149ddd..3f78c8cd 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/FtpDownloadModule.java @@ -48,9 +48,8 @@ public class FtpDownloadModule extends BaseViewModule { */ LiveData getFtpDownloadInfo(Context context) { //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl); - //String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; - String url = "ftp://58.210.178.52:21/battery1.0.0.apk"; - String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath); + //String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath); + String url = "ftp://9.9.9.72:2121/Cyberduck-6.9.4.30164.zip"; singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url); if (singDownloadInfo == null) { @@ -58,7 +57,7 @@ public class FtpDownloadModule extends BaseViewModule { singDownloadInfo.setUrl(url); String name = getFileName(ftpDefUrl); singDownloadInfo.setFileName(name); - singDownloadInfo.setFilePath(filePath + name); + singDownloadInfo.setFilePath(ftpDefPath + name); } else { AppUtil.setConfigValue(context, FTP_PATH_KEY, singDownloadInfo.getFilePath()); AppUtil.setConfigValue(context, FTP_URL_KEY, singDownloadInfo.getUrl()); diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java index 899c39d3..5a86ca0c 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveModule.java @@ -55,7 +55,7 @@ public class M3U8LiveModule extends BaseViewModule { singDownloadInfo.setFilePath(filePath); singDownloadInfo.setFileName(temp.getName()); } else { - AppUtil.setConfigValue(context, M3U8_LIVE_PATH_KEY, singDownloadInfo.getDownloadPath()); + AppUtil.setConfigValue(context, M3U8_LIVE_PATH_KEY, singDownloadInfo.getFilePath()); AppUtil.setConfigValue(context, M3U8_LIVE_URL_KEY, singDownloadInfo.getUrl()); } liveData.postValue(singDownloadInfo); diff --git a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java index 4ce216c8..8bc2331f 100644 --- a/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java +++ b/app/src/main/java/com/arialyy/simple/modlue/AnyRunnModule.java @@ -76,7 +76,7 @@ public class AnyRunnModule { } @Download.onTaskComplete void taskComplete(DownloadTask task) { - L.d(TAG, "path ==> " + task.getDownloadEntity().getDownloadPath()); + L.d(TAG, "path ==> " + task.getDownloadEntity().getFilePath()); L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getFilePath()))); } diff --git a/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java b/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java index 0c42cfcc..9ee9644f 100644 --- a/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java +++ b/app/src/main/java/com/arialyy/simple/widget/SubStateLinearLayout.java @@ -74,7 +74,7 @@ public class SubStateLinearLayout extends LinearLayout implements View.OnClickLi int i = 1; for (DownloadEntity entity : datas) { TextView view = createView(i - 1, entity); - mPosition.put(entity.getDownloadPath(), i); + mPosition.put(entity.getFilePath(), i); addView(view, i); i++; } From 8158c58d6efd1aa0e44d9d844911f3bc7e473f9b Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 6 Jan 2020 19:59:17 +0800 Subject: [PATCH 049/140] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dftp=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=90=8E=EF=BC=8C=E5=88=A0=E9=99=A4=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=AB=AF=E7=9A=84=E6=96=87=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E9=87=8D=E6=96=B0=E4=B8=8B=E8=BD=BD=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/common/AbsBuilderTarget.java | 10 ++++ .../aria/core/common/AbsNormalTarget.java | 17 ++++++- .../core/download/CheckFtpDirEntityUtil.java | 7 ++- .../download/target/FtpDirConfigHandler.java | 2 + .../download/target/FtpDirNormalTarget.java | 14 +++++- .../core/manager/DGTaskWrapperFactory.java | 5 +- DEV_LOG.md | 2 + .../com/arialyy/aria/ftp/AbsFtpInfoTask.java | 8 ++- .../arialyy/aria/ftp/FtpRecordHandler.java | 20 +++++--- .../com/arialyy/aria/ftp/FtpTaskOption.java | 13 +++++ .../aria/ftp/download/FtpDGInfoTask.java | 47 ++++++++++++++++-- .../aria/ftp/upload/FtpUFileInfoTask.java | 38 +++++++++++++- .../ftp/upload/FtpUThreadTaskAdapter.java | 22 ++++----- .../core/download/DownloadGroupEntity.java | 2 +- .../aria/core/group/AbsGroupLoader.java | 3 +- .../aria/core/group/AbsSubDLoadUtil.java | 5 ++ .../aria/core/group/GroupRunState.java | 2 +- .../aria/core/group/GroupSendParams.java | 2 +- .../aria/core/group/SimpleSchedulers.java | 41 +++++++--------- .../aria/core/group/SimpleSubQueue.java | 2 +- .../aria/core/group/SimpleSubRetryQueue.java | 49 +++++++++++++++++++ .../aria/core/loader/AbsNormalTTBuilder.java | 13 ++++- .../arialyy/aria/core/loader/SubLoader.java | 7 ++- .../arialyy/aria/core/task/ThreadTask.java | 8 ++- .../com/arialyy/aria/orm/DelegateFind.java | 5 +- .../com/arialyy/aria/util/RecordUtil.java | 9 ++++ .../group/FTPDirDownloadActivity.java | 5 +- .../simple/core/upload/FtpUploadActivity.java | 3 +- .../simple/core/upload/UploadModule.java | 3 +- 29 files changed, 290 insertions(+), 74 deletions(-) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubRetryQueue.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java index 16eb20c8..a070bf83 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsBuilderTarget.java @@ -27,6 +27,13 @@ public abstract class AbsBuilderTarget extends private BuilderController mStartController; + /** + * 任务操作前调用 + */ + protected void onPre() { + + } + private synchronized BuilderController getController() { if (mStartController == null) { mStartController = new BuilderController(getTaskWrapper()); @@ -58,6 +65,7 @@ public abstract class AbsBuilderTarget extends */ @Override public long add() { + onPre(); return getController().add(); } @@ -68,6 +76,7 @@ public abstract class AbsBuilderTarget extends */ @Override public long create() { + onPre(); return getController().create(); } @@ -82,6 +91,7 @@ public abstract class AbsBuilderTarget extends */ @Override public long setHighestPriority() { + onPre(); return getController().setHighestPriority(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java index 4920b9a6..863e63f6 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/common/AbsNormalTarget.java @@ -31,6 +31,13 @@ import com.arialyy.aria.util.RecordUtil; public abstract class AbsNormalTarget extends AbsTarget implements INormalFeature { + /** + * 任务操作前调用 + */ + protected void onPre() { + + } + /** * 是否忽略权限检查 */ @@ -150,6 +157,7 @@ public abstract class AbsNormalTarget extends Ab */ @Override public void stop() { + onPre(); getController().stop(); } @@ -168,6 +176,7 @@ public abstract class AbsNormalTarget extends Ab * @param newStart true 立即将任务恢复到执行队列中 */ @Override public void resume(boolean newStart) { + onPre(); getController().resume(newStart); } @@ -176,7 +185,7 @@ public abstract class AbsNormalTarget extends Ab */ @Override public void cancel() { - getController().cancel(); + cancel(false); } /** @@ -184,17 +193,19 @@ public abstract class AbsNormalTarget extends Ab */ @Override public void reTry() { + onPre(); getController().reTry(); } /** * 删除任务 * - * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经删除完成的文件 + * @param removeFile {@code true} 不仅删除任务数据库记录,还会删除已经完成的文件 * {@code false}如果任务已经完成,只删除任务数据库记录, */ @Override public void cancel(boolean removeFile) { + onPre(); getController().cancel(removeFile); } @@ -203,11 +214,13 @@ public abstract class AbsNormalTarget extends Ab */ @Override public long reStart() { + onPre(); return getController().reStart(); } @Override public void save() { + onPre(); getController().save(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java index b46da62a..a5d63470 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckFtpDirEntityUtil.java @@ -19,8 +19,8 @@ import android.text.TextUtils; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.inf.ICheckEntityUtil; import com.arialyy.aria.core.inf.IOptionConstant; -import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.CheckUtil; import java.io.File; public class CheckFtpDirEntityUtil implements ICheckEntityUtil { @@ -58,10 +58,9 @@ public class CheckFtpDirEntityUtil implements ICheckEntityUtil { return false; } - if ((mEntity.getDirPath() == null || !mEntity.getDirPath().equals(mWrapper.getDirPathTemp())) - && DbEntity.checkDataExist(DownloadGroupEntity.class, "dirPath=?", + // 检查路径冲突 + if (mWrapper.isNewTask() && !CheckUtil.checkDGPathConflicts(mWrapper.isIgnoreFilePathOccupy(), mWrapper.getDirPathTemp())) { - ALog.e(TAG, String.format("文件夹路径【%s】已被其它任务占用,请重新设置文件夹路径", mWrapper.getDirPathTemp())); return false; } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java index 049001fd..3595874f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirConfigHandler.java @@ -15,10 +15,12 @@ */ package com.arialyy.aria.core.download.target; +import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.inf.AbsTarget; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.CommonUtil; import java.util.List; /** diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java index f221a394..b79d158b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/FtpDirNormalTarget.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.inf.IOptionConstant; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.util.CommonUtil; @@ -27,6 +28,7 @@ import com.arialyy.aria.util.CommonUtil; */ public class FtpDirNormalTarget extends AbsNormalTarget { private FtpDirConfigHandler mConfigHandler; + private FtpOption option; FtpDirNormalTarget(long taskId) { mConfigHandler = new FtpDirConfigHandler<>(this, taskId); @@ -71,8 +73,7 @@ public class FtpDirNormalTarget extends AbsNormalTarget { if (option == null) { throw new NullPointerException("ftp 任务配置为空"); } - option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); - getTaskWrapper().getOptionParams().setParams(option); + this.option = option; return this; } @@ -88,4 +89,13 @@ public class FtpDirNormalTarget extends AbsNormalTarget { public SubTaskManager getSubTaskManager() { return mConfigHandler.getSubTaskManager(); } + + @Override protected void onPre() { + super.onPre(); + if (option == null) { + option = new FtpOption(); + } + option.setUrlEntity(CommonUtil.getFtpUrlInfo(getEntity().getKey())); + getTaskWrapper().getOptionParams().setParams(option); + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java index 4cbf22c9..e877052b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTaskWrapperFactory.java @@ -19,6 +19,7 @@ import com.arialyy.aria.core.download.DGEntityWrapper; import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.orm.DbEntity; +import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.DbDataHelper; import java.util.List; @@ -26,7 +27,7 @@ import java.util.List; * Created by Aria.Lao on 2017/11/1. 组合任务wrapper */ class DGTaskWrapperFactory implements IGroupWrapperFactory { - private static final String TAG = "DTaskWrapperFactory"; + private final String TAG = CommonUtil.getClassName(this); private static volatile DGTaskWrapperFactory INSTANCE = null; private DGTaskWrapperFactory() { @@ -45,7 +46,7 @@ class DGTaskWrapperFactory implements IGroupWrapperFactory 1) { + if (mTaskWrapper.getEntity().getFileSize() > 1 && checkSubOption()) { callback.onSucceed(mEntity.getKey(), new CompleteInfo(200, mTaskWrapper)); } else { super.run(); } } + /** + * 检查子任务的任务设置 + * + * @return true 子任务任务设置成功,false 子任务任务设置失败 + */ + private boolean checkSubOption() { + for (DTaskWrapper wrapper : mTaskWrapper.getSubTaskWrapper()) { + if (wrapper.getTaskOption() == null) { + return false; + } + } + return true; + } + @Override protected String getRemotePath() { return mTaskOption.getUrlEntity().remotePath; } @@ -92,9 +108,14 @@ final class FtpDGInfoTask extends AbsFtpInfoTask try { IFtpUploadInterceptor interceptor = mTaskOption.getUploadInterceptor(); if (interceptor != null) { - /** + /* * true 使用拦截器,false 不使用拦截器 */ List files = new ArrayList<>(); @@ -84,6 +86,7 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask ALog.d(TAG, String.format("删除文件%s,code: %s, msg: %s", b ? "成功" : "失败", client.getReplyCode(), client.getReplyString())); + mTaskOption.setServeFileIsExist(false); } else if (!TextUtils.isEmpty(interceptHandler.getNewFileName())) { ALog.i(TAG, String.format("远端已拥有同名文件,将修改remotePath,原文件名:%s,新文件名:%s", mEntity.getFileName(), interceptHandler.getNewFileName())); @@ -92,6 +95,7 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask + interceptHandler.getNewFileName(); mTaskOption.setNewFileName(interceptHandler.getNewFileName()); + mTaskOption.setServeFileIsExist(false); closeClient(client); run(); return false; @@ -156,7 +160,39 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask record.save(); threadRecord.save(); } + }else { + ALog.d(TAG, "FTP服务器上不存在该文件"); + mTaskWrapper.setNewTask(false); + TaskRecord record = DbDataHelper.getTaskRecord(mTaskWrapper.getKey(), + mTaskWrapper.getEntity().getTaskType()); + if (record != null){ + ALog.w(TAG, String.format("任务记录【%s】已存在,将删除该记录", mTaskWrapper.getKey())); + delTaskRecord(record); + } + mTaskWrapper.setNewTask(true); + record = new TaskRecord(); + record.fileName = mEntity.getFileName(); + record.filePath = mTaskWrapper.getKey(); + record.threadRecords = new ArrayList<>(); + ThreadRecord threadRecord = new ThreadRecord(); + threadRecord.taskKey = record.filePath; + threadRecord.startLocation = 0; + threadRecord.endLocation = mEntity.getFileSize(); + threadRecord.isComplete = false; + + record.save(); + threadRecord.save(); + } + } + + private void delTaskRecord(TaskRecord record){ + List threadRecords = record.threadRecords; + if (threadRecords != null && !threadRecords.isEmpty()){ + for (ThreadRecord tr : threadRecords){ + tr.deleteData(); + } } + record.deleteData(); } @Override protected void onPreComplete(int code) { diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index 6b8d9aa7..8d05431a 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -130,9 +130,10 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (isTimeOut) { fail(new AriaIOException(TAG, "socket连接失败,该问题一般出现于网络断开,客户端重新连接," + "但是服务器端无法创建socket缺没有返回错误码的情况。"), false); - } - if (fa != null) { - fa.close(); + if (fa != null) { + fa.close(); + } + closeTimer(); } isTimeOut = true; } catch (IOException e) { @@ -143,6 +144,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } private void closeTimer() { + ALog.d(TAG, "closeTimer"); if (timer != null && !timer.isShutdown()) { timer.shutdown(); } @@ -158,6 +160,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { fa = new FtpFISAdapter(bis); storeFail = false; startTimer(); + final long fileSize = getThreadConfig().tempFile.length(); try { ALog.d(TAG, String.format("remotePath: %s", remotePath)); client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { @@ -170,6 +173,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (getThreadTask().isBreak() && !isStoped) { isStoped = true; client.abor(); + return; } if (mSpeedBandUtil != null) { mSpeedBandUtil.limitNextBytes(bytesTransferred); @@ -178,19 +182,12 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { } catch (IOException e) { e.printStackTrace(); storeFail = true; - try { - fa.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - closeClient(client); } } }); } catch (IOException e) { String msg = String.format("文件上传错误,错误码为:%s, msg:%s, filePath: %s", client.getReply(), client.getReplyString(), getEntity().getFilePath()); - closeClient(client); e.printStackTrace(); if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); @@ -198,11 +195,14 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { fail(new AriaIOException(TAG, msg, e), !storeFail); } return false; + } finally { + fa.close(); + closeClient(client); } if (storeFail) { return false; } - int reply = client.getReply(); + int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { if (reply != FTPReply.TRANSFER_ABORTED) { fail(new AriaIOException(TAG, diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java index a6b3c0ef..9aa6f10e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DownloadGroupEntity.java @@ -53,7 +53,7 @@ public class DownloadGroupEntity extends AbsGroupEntity { getSubEntities().get(0).getUrl())) { return ITaskWrapper.ERROR; } - return getSubEntities().get(0).getUrl().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR + return (groupHash.startsWith("ftp") || groupHash.startsWith("sftp")) ? ITaskWrapper.D_FTP_DIR : ITaskWrapper.DG_HTTP; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index 7bd3ff25..3969971a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -267,7 +267,8 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { Looper looper = Looper.myLooper(); initState(looper); getState().setSubSize(getWrapper().getSubTaskWrapper().size()); - if (getState().getCompleteNum() == getState().getSubSize()) { + if (getState().getCompleteNum() != 0 + && getState().getCompleteNum() == getState().getSubSize()) { mListener.onComplete(); return; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java index fe5ebb3b..3ec13a77 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.group; import android.os.Handler; +import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.inf.IUtil; @@ -81,6 +82,10 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { return mWrapper.getEntity(); } + public TaskRecord getRecord(){ + return getLoader().getRecord(); + } + @Override public void run() { if (isStop || isCancel) { return; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java index dee9834f..7dcbcc13 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupRunState.java @@ -23,7 +23,7 @@ import java.util.Set; /** * 组合任务执行中的状态信息 */ -public class GroupRunState { +public final class GroupRunState { /** * 子任务数 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java index 82a157ec..303fff3d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/GroupSendParams.java @@ -22,7 +22,7 @@ import com.arialyy.aria.core.task.AbsGroupTask; * Created by lyy on 2017/9/8. * 任务组参数传递 */ -public class GroupSendParams { +public final class GroupSendParams { public GROUP_TASK groupTask; public ENTITY entity; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java index 9634b497..7bf10c0f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSchedulers.java @@ -20,21 +20,20 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import com.arialyy.aria.core.AriaConfig; -import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.config.Configuration; import com.arialyy.aria.core.inf.IThreadStateManager; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.manager.ThreadTaskManager; import com.arialyy.aria.exception.TaskException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.NetUtils; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.io.File; /** * 组合任务子任务调度器,用于调度任务的开始、停止、失败、完成等情况 * 该调度器生命周期和{@link AbsGroupLoaderUtil}生命周期一致 */ -class SimpleSchedulers implements Handler.Callback { +final class SimpleSchedulers implements Handler.Callback { private static final String TAG = "SimpleSchedulers"; private SimpleSubQueue mQueue; private GroupRunState mGState; @@ -86,7 +85,8 @@ class SimpleSchedulers implements Handler.Callback { ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); break; case IThreadStateManager.STATE_FAIL: - handleFail(loaderUtil); + boolean needRetry = b.getBoolean(IThreadStateManager.DATA_RETRY, false); + handleFail(loaderUtil, needRetry); ThreadTaskManager.getInstance().removeSingleTaskThread(mKey, threadName); break; } @@ -98,18 +98,19 @@ class SimpleSchedulers implements Handler.Callback { * 1、子任务失败次数大于等于配置的重试次数,才能认为子任务停止 * 2、stopNum + failNum + completeNum + cacheNum == subSize,则认为组合任务停止 * 3、failNum == subSize,只有全部的子任务都失败了,才能任务组合任务失败 + * + * @param needRetry true 需要重试,false 不需要重试 */ - private synchronized void handleFail(final AbsSubDLoadUtil loaderUtil) { + private synchronized void handleFail(final AbsSubDLoadUtil loaderUtil, boolean needRetry) { Configuration config = Configuration.getInstance(); - long interval = config.dGroupCfg.getSubReTryInterval(); int num = config.dGroupCfg.getSubReTryNum(); boolean isNotNetRetry = config.appCfg.isNotNetRetry(); - final int reTryNum = num; - if ((!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) + if (!needRetry + || (!NetUtils.isConnected(AriaConfig.getInstance().getAPP()) && !isNotNetRetry) || loaderUtil.getLoader() == null // 如果获取不到文件信息,loader为空 - || loaderUtil.getEntity().getFailNum() > reTryNum) { + || loaderUtil.getEntity().getFailNum() > num) { mQueue.removeTaskFromExecQ(loaderUtil); mGState.listener.onSubFail(loaderUtil.getEntity(), new TaskException(TAG, String.format("任务组子任务【%s】下载失败,下载地址【%s】", loaderUtil.getEntity().getFileName(), @@ -127,20 +128,7 @@ class SimpleSchedulers implements Handler.Callback { } return; } - - // 如果获取不到文件信息,loader为空 - final ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(1); - timer.schedule(new Runnable() { - @Override public void run() { - AbsEntity entity = loaderUtil.getEntity(); - if (entity.getFailNum() <= reTryNum) { - ALog.d(TAG, String.format("任务【%s】开始重试", loaderUtil.getEntity().getFileName())); - loaderUtil.reStart(); - } else { - startNext(); - } - } - }, interval, TimeUnit.MILLISECONDS); + SimpleSubRetryQueue.getInstance().offer(loaderUtil); } /** @@ -175,6 +163,11 @@ class SimpleSchedulers implements Handler.Callback { */ private synchronized void handleComplete(AbsSubDLoadUtil loader) { ALog.d(TAG, String.format("子任务【%s】完成", loader.getEntity().getFileName())); + if (loader.getRecord().isBlock) { + File partFile = + new File(String.format(IRecordHandler.SUB_PATH, loader.getRecord().filePath, 0)); + partFile.renameTo(new File(loader.getRecord().filePath)); + } ThreadTaskManager.getInstance().removeTaskThread(loader.getKey()); mGState.listener.onSubComplete(loader.getEntity()); mQueue.removeTaskFromExecQ(loader); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java index 57020f3c..b50c7758 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubQueue.java @@ -29,7 +29,7 @@ import java.util.Set; /** * 组合任务队列,该队列生命周期和{@link AbsGroupLoaderUtil}生命周期一致 */ -class SimpleSubQueue implements ISubQueue { +final class SimpleSubQueue implements ISubQueue { private final String TAG = CommonUtil.getClassName(getClass()); /** * 缓存下载器 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubRetryQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubRetryQueue.java new file mode 100644 index 00000000..0d95cd05 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/SimpleSubRetryQueue.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.core.group; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 子任务重试队列 + */ +final class SimpleSubRetryQueue { + private volatile static SimpleSubRetryQueue INSTANCE = null; + private ExecutorService pool = new ThreadPoolExecutor(5, 100, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + + public synchronized static SimpleSubRetryQueue getInstance() { + if (INSTANCE == null) { + synchronized (SimpleSubRetryQueue.class) { + INSTANCE = new SimpleSubRetryQueue(); + } + } + + return INSTANCE; + } + + private SimpleSubRetryQueue() { + + } + + void offer(AbsSubDLoadUtil subDLoadUtil) { + pool.submit(subDLoadUtil.getLoader()); + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java index 33d7eb86..4f29e41a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalTTBuilder.java @@ -1,6 +1,8 @@ package com.arialyy.aria.core.loader; +import android.os.Bundle; import android.os.Handler; +import android.os.Message; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; import com.arialyy.aria.core.common.AbsNormalEntity; @@ -133,7 +135,16 @@ public abstract class AbsNormalTTBuilder implements IThreadTaskBuilder { if (tr.isComplete) {//该线程已经完成 currentProgress += endL - startL; ALog.d(TAG, String.format("任务【%s】线程__%s__已完成", mWrapper.getKey(), i)); - mStateHandler.obtainMessage(IThreadStateManager.STATE_COMPLETE).sendToTarget(); + Message msg = mStateHandler.obtainMessage(); + msg.what = IThreadStateManager.STATE_COMPLETE; + Bundle b = msg.getData(); + if (b == null){ + b = new Bundle(); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, + CommonUtil.getThreadName(getEntity().getUrl(), tr.threadId)); + msg.setData(b); + msg.sendToTarget(); continue; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java index 5d162beb..f227b2e7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/SubLoader.java @@ -48,6 +48,7 @@ public final class SubLoader implements ILoader, ILoaderVisitor { private IRecordHandler recordHandler; private IThreadTask threadTask; private String parentKey; + private TaskRecord record; public SubLoader(AbsTaskWrapper wrapper, Handler schedulers) { this.wrapper = wrapper; @@ -92,7 +93,7 @@ public final class SubLoader implements ILoader, ILoaderVisitor { } private void handlerTask() { - TaskRecord record = recordHandler.getRecord(wrapper.getEntity().getFileSize()); + record = recordHandler.getRecord(wrapper.getEntity().getFileSize()); if (record.threadRecords != null && !TextUtils.isEmpty(record.filePath) && new File(record.filePath).exists() @@ -123,6 +124,10 @@ public final class SubLoader implements ILoader, ILoaderVisitor { } } + public TaskRecord getRecord(){ + return record; + } + public void setParentKey(String parentKey) { this.parentKey = parentKey; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 8c36ae72..a9463d26 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -86,11 +86,17 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { mEntity = mTaskWrapper.getEntity(); mLastSaveTime = System.currentTimeMillis(); mConfigThreadPool = Executors.newCachedThreadPool(); - isNotNetRetry = AriaConfig.getInstance().getAConfig().isNotNetRetry(); mRangeProgress = mRecord.startLocation; mLastRangeProgress = mRangeProgress; updateInterval = config.updateInterval; + checkFileExist(); + } + + private void checkFileExist() { + if (!getConfig().tempFile.exists()) { + FileUtil.createFile(getConfig().tempFile); + } } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java index 55dbfffc..4f0ba511 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/DelegateFind.java @@ -98,7 +98,7 @@ class DelegateFind extends AbsDelegate { */ List findRelationData(SQLiteDatabase db, Class clazz, String... expression) { - return exeRelationSql(db, clazz, -1, -1, expression); + return exeRelationSql(db, clazz, 1, 10, expression); } /** @@ -111,6 +111,7 @@ class DelegateFind extends AbsDelegate { List findRelationData(SQLiteDatabase db, Class clazz, int page, int num, String... expression) { if (page < 1 || num < 1) { + ALog.w(TAG, "page,num 小于1"); return null; } return exeRelationSql(db, clazz, page, num, expression); @@ -345,6 +346,7 @@ class DelegateFind extends AbsDelegate { List findData(SQLiteDatabase db, Class clazz, int page, int num, String... expression) { if (page < 1 || num < 1) { + ALog.w(TAG, "page, bum 小于1"); return null; } db = checkDb(db); @@ -390,6 +392,7 @@ class DelegateFind extends AbsDelegate { List findDataByFuzzy(SQLiteDatabase db, Class clazz, int page, int num, String conditions) { if (page < 1 || num < 1) { + ALog.w(TAG, "page, bum 小于1"); return null; } db = checkDb(db); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index 3debb45f..7754b050 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -65,6 +65,15 @@ public class RecordUtil { return; } DownloadGroupEntity groupEntity = DbDataHelper.getDGEntityByPath(dirPath); + // 处理组任务存在,而子任务为空的情况 + if (groupEntity == null) { + groupEntity = DbEntity.findFirst(DownloadGroupEntity.class, "dirPath=?", dirPath); + if (groupEntity != null) { + groupEntity.deleteData(); + DbEntity.deleteData(DownloadEntity.class, "groupHash=?", groupEntity.getGroupHash()); + } + return; + } delGroupTaskRecord(groupEntity, removeFile, true); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index a5fb594f..e13632d2 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -37,7 +37,7 @@ import java.security.AlgorithmConstraints; * Created by lyy on 2017/7/6. */ public class FTPDirDownloadActivity extends BaseActivity { - private static final String dir = "ftp://9.9.9.50:2121/upload/测试"; + private static final String dir = "ftp://9.9.9.72:2121/upload/测试"; private SubStateLinearLayout mChildList; private long mTaskId = -1; @@ -81,6 +81,7 @@ public class FTPDirDownloadActivity extends BaseActivity { private String mUrl; private UploadModule mModule; private long mTaskId = -1; - private String user = "ftpuser", pwd = "ftpuser2020"; - //private String user = "lao", pwd = "123456"; + private String user = "lao", pwd = "123456"; @Override protected void init(Bundle savedInstanceState) { setTile("D_FTP 文件上传"); diff --git a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java index 6dcb68d7..deff3f3c 100644 --- a/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/upload/UploadModule.java @@ -38,8 +38,7 @@ public class UploadModule extends BaseViewModule { //String url = AppUtil.getConfigValue(context, FTP_URL_KEY, "ftp://9.9.9.72:2121/aab/你好"); //String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, // Environment.getExternalStorageDirectory().getPath() + "/Download/AndroidAria.db"); - String url = "ftp://101.132.33.64:21/home/ftpuser/videopic/historymonitor/jobs/test"; - //String url = "ftp://9.9.9.72:2121/aab/你好"; + String url = "ftp://9.9.9.72:2121/aab/你好"; String filePath = "/mnt/sdcard/QQMusic-import-1.2.1.zip"; UploadEntity entity = Aria.upload(context).getFirstUploadEntity(filePath); From b1a2f232fa6b00a51415deb339000b3829d180a4 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 6 Jan 2020 21:02:32 +0800 Subject: [PATCH 050/140] =?UTF-8?q?=E4=BC=98=E5=8C=96FtpFileInfoTask?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/arialyy/aria/ftp/AbsFtpInfoTask.java | 92 +++----------- .../com/arialyy/aria/ftp/FtpTaskOption.java | 13 -- .../aria/ftp/download/FtpDFileInfoTask.java | 58 +++++++++ .../aria/ftp/download/FtpDGInfoTask.java | 59 ++++++++- .../aria/ftp/download/FtpDLoaderUtil.java | 3 +- .../FtpDRecordHandler.java} | 12 +- .../aria/ftp/download/FtpSubDLoaderUtil.java | 3 +- .../aria/ftp/upload/FtpUFileInfoTask.java | 114 +++++------------- .../arialyy/aria/ftp/upload/FtpULoader.java | 42 +++++++ .../aria/ftp/upload/FtpULoaderUtil.java | 3 +- .../aria/ftp/upload/FtpURecordHandler.java | 112 +++++++++++++++++ .../aria/core/loader/NormalLoader.java | 8 +- 12 files changed, 330 insertions(+), 189 deletions(-) rename FtpComponent/src/main/java/com/arialyy/aria/ftp/{FtpRecordHandler.java => download/FtpDRecordHandler.java} (91%) create mode 100644 FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpURecordHandler.java diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java index 588df77b..321a36f7 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/AbsFtpInfoTask.java @@ -37,7 +37,6 @@ import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.SSLContextUtil; -import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; @@ -57,7 +56,6 @@ public abstract class AbsFtpInfoTask 0) { - ALog.i(TAG, - String.format("路径【%s】下的文件列表 ===================================", getRemotePath())); - for (FTPFile file : files1) { - ALog.d(TAG, file.toString()); - } - ALog.i(TAG, - "================================= --end-- ==================================="); - } else { - ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString())); - } - closeClient(client); - - failDownload(client, - String.format("文件不存在,url: %s, remotePath:%s", mTaskOption.getUrlEntity().url, - remotePath), null, false); - return; - } - - // 处理拦截功能 - if (!onInterceptor(client, files)) { - closeClient(client); - ALog.d(TAG, "拦截器处理完成任务"); - return; - } - - //为了防止编码错乱,需要使用原始字符串 - if (isUpload && files.length == 0){ - handleFile(getRemotePath(), null); - }else { - mSize = getFileSize(files, client, getRemotePath()); - } - int reply = client.getReplyCode(); - if (!FTPReply.isPositiveCompletion(reply)) { - if (isUpload) { - //服务器上没有该文件路径,表示该任务为新的上传任务 - mTaskWrapper.setNewTask(true); - } else { - closeClient(client); - failDownload(client, "获取文件信息错误,url: " + mTaskOption.getUrlEntity().url, null, true); - return; - } - } - mTaskWrapper.setCode(reply); - if (mSize != 0 && !isUpload) { - mEntity.setFileSize(mSize); - } - onPreComplete(reply); - mEntity.update(); + String convertedRemotePath = CommonUtil.convertFtpChar(charSet, getRemotePath()); + FTPFile[] files = client.listFiles(convertedRemotePath); + handelFileInfo(client, files, convertedRemotePath); } catch (IOException e) { e.printStackTrace(); failDownload(client, "FTP错误信息", e, true); @@ -171,20 +121,6 @@ public abstract class AbsFtpInfoTask 0) { + ALog.i(TAG, + String.format("路径【%s】下的文件列表 ===================================", getRemotePath())); + for (FTPFile file : files1) { + ALog.d(TAG, file.toString()); + } + ALog.i(TAG, + "================================= --end-- ==================================="); + } else { + ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString())); + } + closeClient(client); + + failDownload(client, + String.format("文件不存在,url: %s, remotePath:%s", mTaskOption.getUrlEntity().url, + getRemotePath()), null, false); + return; + } + + // 处理拦截功能 + if (!onInterceptor(client, files)) { + closeClient(client); + ALog.d(TAG, "拦截器处理完成任务"); + return; + } + + //为了防止编码错乱,需要使用原始字符串 + mSize = getFileSize(files, client, getRemotePath()); + int reply = client.getReplyCode(); + if (!FTPReply.isPositiveCompletion(reply)) { + closeClient(client); + failDownload(client, "获取文件信息错误,url: " + mTaskOption.getUrlEntity().url, null, true); + return; + } + mTaskWrapper.setCode(reply); + if (mSize != 0) { + mEntity.setFileSize(mSize); + } + onPreComplete(reply); + mEntity.update(); + } + @Override protected String getRemotePath() { return mTaskOption.getUrlEntity().remotePath; } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGInfoTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGInfoTask.java index 27411f04..7dbf2132 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGInfoTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDGInfoTask.java @@ -19,6 +19,7 @@ import android.net.Uri; import android.text.TextUtils; import aria.apache.commons.net.ftp.FTPClient; import aria.apache.commons.net.ftp.FTPFile; +import aria.apache.commons.net.ftp.FTPReply; import com.arialyy.aria.core.FtpUrlEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.download.DGTaskWrapper; @@ -32,6 +33,8 @@ import com.arialyy.aria.orm.DbEntity; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.RecordUtil; +import java.io.File; +import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; @@ -52,6 +55,60 @@ final class FtpDGInfoTask extends AbsFtpInfoTask 0) { + ALog.i(TAG, + String.format("路径【%s】下的文件列表 ===================================", getRemotePath())); + for (FTPFile file : files1) { + ALog.d(TAG, file.toString()); + } + ALog.i(TAG, + "================================= --end-- ==================================="); + } else { + ALog.w(TAG, String.format("获取文件列表失败,msg:%s", client.getReplyString())); + } + closeClient(client); + + failDownload(client, + String.format("文件不存在,url: %s, remotePath:%s", mTaskOption.getUrlEntity().url, + getRemotePath()), null, false); + return; + } + + // 处理拦截功能 + if (!onInterceptor(client, files)) { + closeClient(client); + ALog.d(TAG, "拦截器处理完成任务"); + return; + } + + //为了防止编码错乱,需要使用原始字符串 + mSize = getFileSize(files, client, getRemotePath()); + int reply = client.getReplyCode(); + if (!FTPReply.isPositiveCompletion(reply)) { + closeClient(client); + failDownload(client, "获取文件信息错误,url: " + mTaskOption.getUrlEntity().url, null, true); + return; + } + mTaskWrapper.setCode(reply); + if (mSize != 0) { + mEntity.setFileSize(mSize); + } + onPreComplete(reply); + mEntity.update(); + } + /** * 检查子任务的任务设置 * @@ -110,7 +167,7 @@ final class FtpDGInfoTask extends AbsFtpInfoTask ALog.d(TAG, String.format("删除文件%s,code: %s, msg: %s", b ? "成功" : "失败", client.getReplyCode(), client.getReplyString())); - mTaskOption.setServeFileIsExist(false); } else if (!TextUtils.isEmpty(interceptHandler.getNewFileName())) { ALog.i(TAG, String.format("远端已拥有同名文件,将修改remotePath,原文件名:%s,新文件名:%s", mEntity.getFileName(), interceptHandler.getNewFileName())); @@ -95,7 +114,6 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask + interceptHandler.getNewFileName(); mTaskOption.setNewFileName(interceptHandler.getNewFileName()); - mTaskOption.setServeFileIsExist(false); closeClient(client); run(); return false; @@ -120,84 +138,16 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask */ @Override protected void handleFile(String remotePath, FTPFile ftpFile) { super.handleFile(remotePath, ftpFile); - if (ftpFile != null) { - //远程文件已完成 - if (ftpFile.getSize() == mEntity.getFileSize()) { - isComplete = true; - ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】"); - } else if (ftpFile.getSize() == 0) { - mTaskWrapper.setNewTask(true); - ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】,但文件长度为0,重新上传该文件"); - } else { - ALog.w(TAG, "FTP服务器已存在未完成的文件【" - + ftpFile.getName() - + ",size: " - + ftpFile.getSize() - + "】" - + "尝试从位置:" - + (ftpFile.getSize() - 1) - + "开始上传"); - mTaskWrapper.setNewTask(false); - - // 修改记录 - TaskRecord record = DbDataHelper.getTaskRecord(mTaskWrapper.getKey(), - mTaskWrapper.getEntity().getTaskType()); - if (record == null) { - record = new TaskRecord(); - record.fileName = mEntity.getFileName(); - record.filePath = mTaskWrapper.getKey(); - record.threadRecords = new ArrayList<>(); - } - ThreadRecord threadRecord; - if (record.threadRecords == null || record.threadRecords.isEmpty()) { - threadRecord = new ThreadRecord(); - threadRecord.taskKey = record.filePath; - } else { - threadRecord = record.threadRecords.get(0); - } - //修改本地保存的停止地址为服务器上对应文件的大小 - threadRecord.startLocation = ftpFile.getSize() - 1; - record.save(); - threadRecord.save(); - } - }else { - ALog.d(TAG, "FTP服务器上不存在该文件"); - mTaskWrapper.setNewTask(false); - TaskRecord record = DbDataHelper.getTaskRecord(mTaskWrapper.getKey(), - mTaskWrapper.getEntity().getTaskType()); - if (record != null){ - ALog.w(TAG, String.format("任务记录【%s】已存在,将删除该记录", mTaskWrapper.getKey())); - delTaskRecord(record); - } - mTaskWrapper.setNewTask(true); - record = new TaskRecord(); - record.fileName = mEntity.getFileName(); - record.filePath = mTaskWrapper.getKey(); - record.threadRecords = new ArrayList<>(); - ThreadRecord threadRecord = new ThreadRecord(); - threadRecord.taskKey = record.filePath; - threadRecord.startLocation = 0; - threadRecord.endLocation = mEntity.getFileSize(); - threadRecord.isComplete = false; - - record.save(); - threadRecord.save(); - } - } - - private void delTaskRecord(TaskRecord record){ - List threadRecords = record.threadRecords; - if (threadRecords != null && !threadRecords.isEmpty()){ - for (ThreadRecord tr : threadRecords){ - tr.deleteData(); - } + this.ftpFile = ftpFile; + if (ftpFile != null && ftpFile.getSize() == mEntity.getFileSize()) { + isComplete = true; } - record.deleteData(); } @Override protected void onPreComplete(int code) { super.onPreComplete(code); - callback.onSucceed(mEntity.getKey(), - new CompleteInfo(isComplete ? CODE_COMPLETE : code, mTaskWrapper)); + CompleteInfo info = new CompleteInfo(isComplete ? CODE_COMPLETE : code, mTaskWrapper); + info.obj = ftpFile; + callback.onSucceed(mEntity.getKey(), info); } } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoader.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoader.java index 21076650..442e7e67 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoader.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoader.java @@ -15,19 +15,56 @@ */ package com.arialyy.aria.ftp.upload; +import android.os.Handler; +import aria.apache.commons.net.ftp.FTPFile; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.common.CompleteInfo; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.IInfoTask; +import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.loader.NormalLoader; +import com.arialyy.aria.core.manager.ThreadTaskManager; +import com.arialyy.aria.core.task.IThreadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.exception.BaseException; final class FtpULoader extends NormalLoader { + private FTPFile ftpFile; + FtpULoader(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); } + @Override + protected void startThreadTask() { + // 检查记录 + ((FtpURecordHandler) mRecordHandler).setFtpFile(ftpFile); + if (mRecordHandler.checkTaskCompleted()) { + mRecord.deleteData(); + isComplete = true; + getListener().onComplete(); + return; + } + mRecord = mRecordHandler.getRecord(getFileSize()); + + // 初始化线程状态管理器 + mStateManager.setLooper(mRecord, getLooper()); + getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, + new Handler(getLooper(), mStateManager.getHandlerCallback()))); + mStateManager.updateCurrentProgress(getEntity().getCurrentProgress()); + + if (mStateManager.getCurrentProgress() > 0) { + getListener().onResume(mStateManager.getCurrentProgress()); + } else { + getListener().onStart(mStateManager.getCurrentProgress()); + } + + // 启动线程任务 + for (IThreadTask threadTask : getTaskList()) { + ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask); + } + } + @Override public void addComponent(IInfoTask infoTask) { mInfoTask = infoTask; infoTask.setCallback(new IInfoTask.Callback() { @@ -35,6 +72,7 @@ final class FtpULoader extends NormalLoader { if (info.code == FtpUFileInfoTask.CODE_COMPLETE) { getListener().onComplete(); } else { + ftpFile = (FTPFile) info.obj; startThreadTask(); } } @@ -44,4 +82,8 @@ final class FtpULoader extends NormalLoader { } }); } + + @Override public void addComponent(IRecordHandler recordHandler) { + mRecordHandler = recordHandler; + } } diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java index b8fe8589..a1295fb6 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpULoaderUtil.java @@ -22,7 +22,6 @@ import com.arialyy.aria.core.loader.LoaderStructure; import com.arialyy.aria.core.loader.NormalThreadStateManager; import com.arialyy.aria.core.upload.UTaskWrapper; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.ftp.FtpRecordHandler; import com.arialyy.aria.ftp.FtpTaskOption; /** @@ -42,7 +41,7 @@ public final class FtpULoaderUtil extends AbsNormalLoaderUtil { @Override public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new FtpRecordHandler(getTaskWrapper())) + structure.addComponent(new FtpURecordHandler((UTaskWrapper) getTaskWrapper())) .addComponent(new NormalThreadStateManager(getListener())) .addComponent(new FtpUFileInfoTask((UTaskWrapper) getTaskWrapper())) .addComponent(new FtpUTTBuilder(getTaskWrapper())); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpURecordHandler.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpURecordHandler.java new file mode 100644 index 00000000..aa452338 --- /dev/null +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpURecordHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.ftp.upload; + +import aria.apache.commons.net.ftp.FTPFile; +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.upload.UTaskWrapper; +import com.arialyy.aria.util.ALog; +import com.arialyy.aria.util.RecordUtil; +import java.util.ArrayList; + +/** + * 上传任务记录处理器 + */ +final class FtpURecordHandler extends RecordHandler { + private FTPFile ftpFile; + + FtpURecordHandler(UTaskWrapper wrapper) { + super(wrapper); + } + + void setFtpFile(FTPFile ftpFile) { + this.ftpFile = ftpFile; + } + + @Override public void handlerTaskRecord(TaskRecord record) { + if (record.threadRecords == null || record.threadRecords.isEmpty()) { + record.threadRecords = new ArrayList<>(); + record.threadRecords.add( + createThreadRecord(record, 0, ftpFile == null ? 0 : ftpFile.getSize(), getFileSize())); + } + + if (ftpFile != null) { + //远程文件已完成 + if (ftpFile.getSize() == getFileSize()) { + record.threadRecords.get(0).isComplete = true; + ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】"); + } else if (ftpFile.getSize() == 0) { + getWrapper().setNewTask(true); + ALog.d(TAG, "FTP服务器上已存在该文件【" + ftpFile.getName() + "】,但文件长度为0,重新上传该文件"); + } else { + ALog.w(TAG, "FTP服务器已存在未完成的文件【" + + ftpFile.getName() + + ",size: " + + ftpFile.getSize() + + "】" + + "尝试从位置:" + + (ftpFile.getSize() - 1) + + "开始上传"); + getWrapper().setNewTask(false); + + // 修改记录 + ThreadRecord threadRecord = record.threadRecords.get(0); + //修改本地保存的停止地址为服务器上对应文件的大小 + threadRecord.startLocation = ftpFile.getSize() - 1; + } + } else { + ALog.d(TAG, "FTP服务器上不存在该文件"); + getWrapper().setNewTask(true); + ThreadRecord tr = record.threadRecords.get(0); + tr.startLocation = 0; + tr.endLocation = getFileSize(); + tr.isComplete = false; + } + } + + @Override + public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { + ThreadRecord tr; + tr = new ThreadRecord(); + tr.taskKey = record.filePath; + tr.threadId = threadId; + tr.startLocation = startL; + tr.isComplete = false; + tr.threadType = getWrapper().getEntity().getTaskType(); + tr.endLocation = getFileSize(); + tr.blockLen = RecordUtil.getBlockLen(getFileSize(), threadId, record.threadNum); + return tr; + } + + @Override public TaskRecord createTaskRecord(int threadNum) { + TaskRecord record = new TaskRecord(); + record.fileName = getEntity().getFileName(); + record.filePath = getEntity().getFilePath(); + record.threadRecords = new ArrayList<>(); + record.threadNum = threadNum; + record.isBlock = false; + record.taskType = getWrapper().getEntity().getTaskType(); + record.isGroupRecord = getEntity().isGroupChild(); + + return record; + } + + @Override public int initTaskThreadNum() { + return 1; + } +} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index 255beabc..3b9a3c57 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -36,7 +36,7 @@ import java.io.File; */ public class NormalLoader extends AbsNormalLoader { private int startThreadNum; //启动的线程数 - private boolean isComplete = false; + protected boolean isComplete = false; private Looper looper; public NormalLoader(AbsTaskWrapper wrapper, IEventListener listener) { @@ -82,6 +82,10 @@ public class NormalLoader extends AbsNormalLoader { // } //} + protected Looper getLooper() { + return looper; + } + /** * 启动单线程任务 */ @@ -103,6 +107,7 @@ public class NormalLoader extends AbsNormalLoader { if (file.getParentFile() != null && !file.getParentFile().exists()) { FileUtil.createDir(file.getPath()); } + // 处理记录、初始化状态管理器 mRecord = mRecordHandler.getRecord(getFileSize()); mStateManager.setLooper(mRecord, looper); getTaskList().addAll(mTTBuilder.buildThreadTask(mRecord, @@ -116,6 +121,7 @@ public class NormalLoader extends AbsNormalLoader { getListener().onStart(mStateManager.getCurrentProgress()); } + // 启动线程任务 for (IThreadTask threadTask : getTaskList()) { ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), threadTask); } From 432ae1c1456761a1e9ad2be2c3be7d61fc6e1ea7 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Tue, 7 Jan 2020 21:56:35 +0800 Subject: [PATCH 051/140] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E4=B8=AD=E7=9A=84=E4=BB=BB=E5=8A=A1api=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96=E5=89=A9=E4=BD=99=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E7=9A=84api=20=E4=BF=AE=E5=A4=8D=E9=87=8D=E6=9E=84loa?= =?UTF-8?q?der=E5=AF=BC=E8=87=B4=E7=9A=84m3u8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aria/core/download/DownloadReceiver.java | 20 ++++ .../download/target/GroupNormalTarget.java | 33 +++++++ .../arialyy/aria/core/queue/AbsTaskQueue.java | 30 ++++++ .../arialyy/aria/core/queue/DTaskQueue.java | 1 + .../aria/core/upload/UploadReceiver.java | 10 ++ DEV_LOG.md | 2 + .../aria/ftp/download/FtpDLoaderUtil.java | 2 +- .../aria/ftp/upload/FtpUFileInfoTask.java | 5 +- .../ftp/upload/FtpUThreadTaskAdapter.java | 23 +++-- .../aria/m3u8/live/LiveRecordHandler.java | 99 +++++++++++++++++++ .../aria/m3u8/live/LiveStateManager.java | 7 +- .../aria/m3u8/live/M3U8LiveLoader.java | 52 +++++----- .../arialyy/aria/m3u8/live/M3U8LiveUtil.java | 5 +- .../arialyy/aria/m3u8/vod/M3U8VodLoader.java | 39 +++++--- .../arialyy/aria/m3u8/vod/M3U8VodUtil.java | 3 +- .../VodRecordHandler.java} | 19 ++-- .../aria/m3u8/vod/VodStateManager.java | 8 +- .../arialyy/aria/core/common/AbsEntity.java | 17 ++++ .../aria/core/common/RecordHelper.java | 16 ++- .../aria/core/common/SubThreadConfig.java | 2 +- .../aria/core/group/AbsGroupLoader.java | 2 +- .../aria/core/listener/BaseListener.java | 7 ++ .../core/listener/DownloadGroupListener.java | 18 ++-- .../aria/core/loader/AbsNormalLoader.java | 39 ++++---- .../aria/core/loader/NormalLoader.java | 3 + .../com/arialyy/aria/core/task/AbsTask.java | 36 ++++++- .../arialyy/aria/core/task/ThreadTask.java | 43 ++++---- .../com/arialyy/aria/util/CommonUtil.java | 34 +++++++ .../com/arialyy/aria/util/ComponentUtil.java | 1 + .../core/download/SingleTaskActivity.java | 1 + .../download/group/DownloadGroupActivity.java | 3 +- .../download/m3u8/M3U8LiveDLoadActivity.java | 6 +- .../download/m3u8/M3U8VodDLoadActivity.java | 2 +- .../res/layout/activity_download_group.xml | 5 + .../main/res/layout/activity_m3u8_live.xml | 10 ++ app/src/main/res/layout/activity_single.xml | 5 + .../main/res/layout/layout_content_single.xml | 15 +++ 37 files changed, 495 insertions(+), 128 deletions(-) create mode 100644 M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java rename M3U8Component/src/main/java/com/arialyy/aria/m3u8/{M3U8RecordHandler.java => vod/VodRecordHandler.java} (88%) diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 5382cd4f..cc71525b 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -36,6 +36,8 @@ import com.arialyy.aria.core.download.target.HttpNormalTarget; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.ReceiverType; +import com.arialyy.aria.core.queue.DGroupTaskQueue; +import com.arialyy.aria.core.queue.DTaskQueue; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.orm.DbEntity; @@ -302,6 +304,24 @@ public class DownloadReceiver extends AbsReceiver { return DbDataHelper.getDGEntityByHash(url); } + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getDRunningTask() { + return DTaskQueue.getInstance().getRunningTask(DownloadEntity.class); + } + + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getDGRunningTask() { + return DGroupTaskQueue.getInstance().getRunningTask(DownloadGroupEntity.class); + } + /** * 下载任务是否存在 * diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java index 4d710cc6..06e9b8ed 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/target/GroupNormalTarget.java @@ -17,8 +17,10 @@ package com.arialyy.aria.core.download.target; import com.arialyy.aria.core.common.AbsNormalTarget; import com.arialyy.aria.core.common.HttpOption; +import com.arialyy.aria.core.download.DGTaskWrapper; import com.arialyy.aria.core.manager.SubTaskManager; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.util.ALog; import java.util.List; /** @@ -101,6 +103,37 @@ public class GroupNormalTarget extends AbsNormalTarget { return mConfigHandler.setSubFileName(subTaskFileName); } + /** + * 如果无法获取到组合任务到总长度,请调用该方法, + * 请注意: + * 1、如果组合任务到子任务数过多,请不要使用该标志位,否则Aria将需要消耗大量的时间获取组合任务的总长度,直到获取完成组合任务总长度后才会执行下载。 + * 2、如果你的知道组合任务的总长度,请使用{@link #setFileSize(long)}设置组合任务的长度。 + * 3、由于网络或其它原因的存在,这种方式获取的组合任务大小有可能是不准确的。 + */ + public GroupNormalTarget unknownSize() { + ((DGTaskWrapper) getTaskWrapper()).setUnknownSize(true); + return this; + } + + /** + * 任务组总任务大小,任务组是一个抽象的概念,没有真实的数据实体,任务组的大小是Aria动态获取子任务大小相加而得到的, + * 如果你知道当前任务组总大小,你也可以调用该方法给任务组设置大小 + * + * 为了更好的用户体验,组合任务最好设置文件大小,默认需要强制设置文件大小。如果无法获取到总长度,请调用{@link #unknownSize()} + * + * @param fileSize 任务组总大小 + */ + public GroupNormalTarget setFileSize(long fileSize) { + if (fileSize <= 0) { + ALog.e(TAG, "文件大小不能小于 0"); + return this; + } + if (getEntity().getFileSize() <= 1 || getEntity().getFileSize() != fileSize) { + getEntity().setFileSize(fileSize); + } + return this; + } + @Override public boolean isRunning() { return mConfigHandler.isRunning(); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java index 203ee0a5..695c7bf4 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/AbsTaskQueue.java @@ -16,6 +16,8 @@ package com.arialyy.aria.core.queue; +import android.text.TextUtils; +import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.core.manager.TaskWrapperManager; @@ -31,6 +33,8 @@ import com.arialyy.aria.core.task.DownloadTask; import com.arialyy.aria.core.task.UploadTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.util.ALog; +import java.util.ArrayList; +import java.util.List; /** * Created by lyy on 2017/2/23. 任务队列 @@ -64,7 +68,33 @@ public abstract class AbsTaskQueue List getRunningTask(Class type) { + List exeTask = mExecutePool.getAllTask(); + List cacheTask = mCachePool.getAllTask(); + List entities = new ArrayList<>(); + if (exeTask != null && !exeTask.isEmpty()) { + for (TASK task : exeTask) { + entities.add((T) task.getTaskWrapper().getEntity()); + } + } + if (cacheTask != null && !cacheTask.isEmpty()) { + for (TASK task : cacheTask) { + entities.add((T) task.getTaskWrapper().getEntity()); + } + } + return entities.isEmpty() ? null : entities; + } + @Override public boolean taskIsRunning(String key) { + if (TextUtils.isEmpty(key)) { + ALog.w(TAG, "key为空,无法确认任务是否执行"); + return false; + } TASK task = mExecutePool.getTask(key); if (task == null && ThreadTaskManager.getInstance().taskIsRunning(key)) { ThreadTaskManager.getInstance().removeTaskThread(key); diff --git a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java index d275bbc5..499a37dc 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java +++ b/Aria/src/main/java/com/arialyy/aria/core/queue/DTaskQueue.java @@ -18,6 +18,7 @@ package com.arialyy.aria.core.queue; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.download.DTaskWrapper; +import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.event.DMaxNumEvent; import com.arialyy.aria.core.event.Event; import com.arialyy.aria.core.event.EventMsgUtil; diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index 7b5aea7b..3eaa62ae 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -28,6 +28,7 @@ import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.event.EventMsgUtil; import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.ReceiverType; +import com.arialyy.aria.core.queue.UTaskQueue; import com.arialyy.aria.core.scheduler.TaskSchedulers; import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.upload.target.FtpBuilderTarget; @@ -230,6 +231,15 @@ public class UploadReceiver extends AbsReceiver { ITask.UPLOAD)); } + /** + * 获取执行中的任务 + * + * @return 没有执行中的任务,返回null + */ + public List getURunningTask() { + return UTaskQueue.getInstance().getRunningTask(UploadEntity.class); + } + /** * 删除所有任务 * diff --git a/DEV_LOG.md b/DEV_LOG.md index e94c8782..fac48057 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -5,6 +5,8 @@ - 添加ftp服务器标志 https://github.com/AriaLyy/Aria/issues/580 - 重构loader模块,让loader模块的代码更加清晰,去除一些不必要的线程创建 - 修复ftp上传完成后,删除服务器端的文件,无法重新下载的问题 + - 增加获取执行中的任务api,详情见:https://aria.laoyuyu.me/aria_doc/api/task_list.html + - 增加获取剩余时间的api,详情见:https://aria.laoyuyu.me/aria_doc/start/task_explain.html + v_3.8.1 (2019/12/22) - 修复一个表创建失败的问题 https://github.com/AriaLyy/Aria/issues/570 - 修复一个非分块模式下导致下载失败的问题 https://github.com/AriaLyy/Aria/issues/571 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java index 969de722..160057f5 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderUtil.java @@ -42,7 +42,7 @@ public final class FtpDLoaderUtil extends AbsNormalLoaderUtil { public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new FtpDRecordHandler(getTaskWrapper())) + structure.addComponent(new FtpDRecordHandler((DTaskWrapper) getTaskWrapper())) .addComponent(new NormalThreadStateManager(getListener())) .addComponent(new FtpDFileInfoTask((DTaskWrapper) getTaskWrapper())) .addComponent(new FtpDTTBuilder(getTaskWrapper())); diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java index 6069f5ee..58ecaef4 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUFileInfoTask.java @@ -60,10 +60,7 @@ final class FtpUFileInfoTask extends AbsFtpInfoTask return; } - //为了防止编码错乱,需要使用原始字符串 - if (files.length == 0) { - handleFile(getRemotePath(), null); - } + handleFile(getRemotePath(), files.length == 0 ? null : files[0]); int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { //服务器上没有该文件路径,表示该任务为新的上传任务 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java index 8d05431a..c948a6be 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/upload/FtpUThreadTaskAdapter.java @@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit; */ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private String dir, remotePath; - private boolean storeFail = false; + private boolean storeSuccess = false; private ScheduledThreadPoolExecutor timer; private FTPClient client = null; private boolean isTimeOut = true; @@ -77,7 +77,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { file.seek(getThreadRecord().startLocation); } boolean complete = upload(file); - if (!complete || getThreadTask().isBreak()) { + if (getThreadTask().isBreak()) { + return; + } + if (!complete){ + fail(new AriaIOException(TAG, "ftp文件上传失败"), false); return; } ALog.i(TAG, @@ -158,12 +162,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { private boolean upload(final BufferedRandomAccessFile bis) throws IOException { fa = new FtpFISAdapter(bis); - storeFail = false; + storeSuccess = false; startTimer(); - final long fileSize = getThreadConfig().tempFile.length(); try { ALog.d(TAG, String.format("remotePath: %s", remotePath)); - client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { + storeSuccess = client.storeFile(remotePath, fa, new OnFtpInputStreamListener() { boolean isStoped = false; @Override public void onFtpInputStream(FTPClient client, long totalBytesTransferred, @@ -181,7 +184,7 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { progress(bytesTransferred); } catch (IOException e) { e.printStackTrace(); - storeFail = true; + storeSuccess = false; } } }); @@ -192,15 +195,11 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { if (e.getMessage().contains("AriaIOException caught while copying")) { e.printStackTrace(); } else { - fail(new AriaIOException(TAG, msg, e), !storeFail); + fail(new AriaIOException(TAG, msg, e), !storeSuccess); } return false; } finally { fa.close(); - closeClient(client); - } - if (storeFail) { - return false; } int reply = client.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { @@ -212,6 +211,6 @@ final class FtpUThreadTaskAdapter extends BaseFtpThreadTaskAdapter { closeClient(client); return false; } - return true; + return storeSuccess; } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java new file mode 100644 index 00000000..206c2a97 --- /dev/null +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveRecordHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.arialyy.aria.m3u8.live; + +import com.arialyy.aria.core.TaskRecord; +import com.arialyy.aria.core.ThreadRecord; +import com.arialyy.aria.core.common.RecordHandler; +import com.arialyy.aria.core.loader.IRecordHandler; +import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.m3u8.M3U8TaskOption; +import com.arialyy.aria.util.RecordUtil; +import java.util.ArrayList; + +/** + * 直播m3u8文件处理器 + */ +final class LiveRecordHandler extends RecordHandler { + private M3U8TaskOption mOption; + + LiveRecordHandler(AbsTaskWrapper wrapper) { + super(wrapper); + } + + public void setOption(M3U8TaskOption option) { + mOption = option; + } + + @Override public void onPre() { + super.onPre(); + RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); + } + + /** + * @deprecated 直播文件不需要处理任务记录 + */ + @Deprecated + @Override public void handlerTaskRecord(TaskRecord record) { + if (record.threadRecords == null) { + record.threadRecords = new ArrayList<>(); + } + } + + /** + * @deprecated 交由{@link #createThreadRecord(TaskRecord, String, int)} 处理 + */ + @Override + @Deprecated + public ThreadRecord createThreadRecord(TaskRecord record, int threadId, long startL, long endL) { + return null; + } + + /** + * 创建线程记录 + * + * @param taskRecord 任务记录 + * @param tsUrl ts下载地址 + * @param threadId 线程id + */ + ThreadRecord createThreadRecord(TaskRecord taskRecord, String tsUrl, int threadId) { + ThreadRecord tr = new ThreadRecord(); + tr.taskKey = taskRecord.filePath; + tr.isComplete = false; + tr.tsUrl = tsUrl; + tr.threadType = getEntity().getTaskType(); + tr.threadId = threadId; + tr.startLocation = 0; + taskRecord.threadRecords.add(tr); + return tr; + } + + @Override public TaskRecord createTaskRecord(int threadNum) { + TaskRecord record = new TaskRecord(); + record.fileName = getEntity().getFileName(); + record.filePath = getEntity().getFilePath(); + record.threadRecords = new ArrayList<>(); + record.threadNum = threadNum; + record.isBlock = true; + record.taskType = getEntity().getTaskType(); + record.bandWidth = mOption.getBandWidth(); + return record; + } + + @Override public int initTaskThreadNum() { + return 1; + } +} diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java index ab7901c5..03f28116 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/LiveStateManager.java @@ -15,6 +15,7 @@ */ package com.arialyy.aria.m3u8.live; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -80,7 +81,11 @@ final class LiveStateManager implements IThreadStateManager { msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex); break; case STATE_RUNNING: - mProgress += (long) msg.obj; + Bundle b = msg.getData(); + if (b != null) { + long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0); + mProgress += len; + } break; case STATE_FAIL: mLoader.notifyLock(false, peerIndex); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 0bd13449..4768faca 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -68,11 +68,9 @@ final class M3U8LiveLoader extends BaseM3U8Loader { private Condition mCondition = LOCK.newCondition(); private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); private ExtInfo mCurExtInfo; - private LiveStateManager mManager; private M3U8InfoTask mInfoTask; private ScheduledThreadPoolExecutor mTimer; private List mPeerUrls = new ArrayList<>(); - private Looper mLooper; M3U8LiveLoader(DTaskWrapper wrapper, M3U8Listener listener) { super(wrapper, listener); @@ -94,8 +92,22 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (isBreak()) { return; } - mLooper = looper; + + // 处理记录 + getRecordHandler().setOption(mM3U8Option); + mRecord = getRecordHandler().getRecord(0); + + // 初始化状态管理器 + getStateManager().setLooper(mRecord, looper); + getStateManager().setLoader(this); + mStateHandler = new Handler(looper, getStateManager().getHandlerCallback()); + + // 循环获取直播文件列表 startLoaderLiveInfo(); + + // 启动定时器 + startTimer(); + new Thread(new Runnable() { @Override public void run() { String cacheDir = getCacheDir(); @@ -127,6 +139,14 @@ final class M3U8LiveLoader extends BaseM3U8Loader { }).start(); } + @Override protected LiveStateManager getStateManager() { + return (LiveStateManager) super.getStateManager(); + } + + private LiveRecordHandler getRecordHandler() { + return (LiveRecordHandler) mRecordHandler; + } + @Override public long getFileSize() { return mTempFile.length(); } @@ -165,20 +185,14 @@ final class M3U8LiveLoader extends BaseM3U8Loader { * 配置config */ private ThreadTask createThreadTask(String cacheDir, int indexId, String tsUrl) { - ThreadRecord record = new ThreadRecord(); - record.taskKey = mRecord.filePath; - record.isComplete = false; - record.tsUrl = tsUrl; - record.threadType = getEntity().getTaskType(); - record.threadId = indexId; - mRecord.threadRecords.add(record); + ThreadRecord tr = getRecordHandler().createThreadRecord(mRecord, tsUrl, indexId); SubThreadConfig config = new SubThreadConfig(); config.url = tsUrl; config.tempFile = new File(getTsFilePath(cacheDir, indexId)); config.isBlock = mRecord.isBlock; config.taskWrapper = mTaskWrapper; - config.record = record; + config.record = tr; config.stateHandler = mStateHandler; config.peerIndex = indexId; config.threadType = SubThreadConfig.getThreadType(ITaskWrapper.M3U8_LIVE); @@ -219,15 +233,10 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (isSuccess) { // 合并成功,删除缓存文件 for (String pp : partPath) { - File f = new File(pp); - if (f.exists()) { - f.delete(); - } + FileUtil.deleteFile(pp); } File cDir = new File(cacheDir); - if (cDir.exists()) { - cDir.delete(); - } + FileUtil.deleteDir(cDir); return true; } else { ALog.e(TAG, "合并失败"); @@ -323,10 +332,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader { * 需要在{@link #addComponent(IRecordHandler)} 后调用 */ @Override public void addComponent(IThreadStateManager threadState) { - mManager = (LiveStateManager) threadState; - mManager.setLooper(mRecordHandler.getRecord(0), mLooper); - mManager.setLoader(this); - mStateHandler = new Handler(mLooper, mManager.getHandlerCallback()); + mStateManager = threadState; } /** @@ -344,7 +350,7 @@ final class M3U8LiveLoader extends BaseM3U8Loader { if (mInfoTask == null) { throw new NullPointerException(("文件信息组件为空")); } - if (mManager == null) { + if (mStateManager == null) { throw new NullPointerException("任务状态管理组件为空"); } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index eb7327a5..a447be99 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -23,9 +23,7 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoTask; import com.arialyy.aria.m3u8.M3U8Listener; -import com.arialyy.aria.m3u8.M3U8RecordHandler; import com.arialyy.aria.m3u8.M3U8TaskOption; -import com.arialyy.aria.util.CommonUtil; /** * M3U8直播文件下载工具,对于直播来说,需要定时更新m3u8文件 @@ -37,7 +35,6 @@ import com.arialyy.aria.util.CommonUtil; * 5、不处理直播切片下载失败的状态 */ public class M3U8LiveUtil extends AbsNormalLoaderUtil { - private final String TAG = CommonUtil.getClassName(getClass()); public M3U8LiveUtil(AbsTaskWrapper wrapper, IEventListener listener) { super(wrapper, listener); @@ -57,7 +54,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil { @Override public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new M3U8RecordHandler(getTaskWrapper())) + structure.addComponent(new LiveRecordHandler(getTaskWrapper())) .addComponent(new M3U8InfoTask(getTaskWrapper())) .addComponent(new LiveStateManager(getTaskWrapper(), getListener())); structure.accept(getLoader()); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index d3f46333..832ecb53 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -73,7 +73,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { private Condition mJumpCondition = JUMP_LOCK.newCondition(); private SparseArray mBeforePeer = new SparseArray<>(); private SparseArray mAfterPeer = new SparseArray<>(); - private VodStateManager mManager; private PeerIndexEvent mCurrentEvent; private String mCacheDir; private int aIndex = 0, bIndex = 0; @@ -136,11 +135,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { } } - @Override protected void onPostPre() { - super.onPostPre(); - initData(); - } - @Override public boolean isBreak() { return super.isBreak() || isDestroy; } @@ -154,6 +148,22 @@ final class M3U8VodLoader extends BaseM3U8Loader { } private void startThreadTask() { + // 处理任务记录 + ((VodRecordHandler) mRecordHandler).setOption(mM3U8Option); + mRecord = mRecordHandler.getRecord(0); + + // 处理任务管理器 + mStateHandler = new Handler(mLooper, getStateManager().getHandlerCallback()); + getStateManager().setVodLoader(this); + getStateManager().setLooper(mRecord, mLooper); + + // 初始化ts数据 + initData(); + + // 启动定时器 + startTimer(); + + // 启动线程开始下载ts切片 Thread th = new Thread(new Runnable() { @Override public void run() { while (!isBreak()) { @@ -258,7 +268,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { mCompleteNum++; } } - mManager.updateStateCount(); + getStateManager().updateStateCount(); if (mCompleteNum <= 0) { getListener().onStart(0); } else { @@ -416,7 +426,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { String.format("beforeSize = %s, afterSize = %s, mCompleteNum = %s", mBeforePeer.size(), mAfterPeer.size(), mCompleteNum)); ALog.i(TAG, String.format("完成处理数据的操作,将优先下载【%s】之后的切片", mCurrentEvent.peerIndex)); - mManager.updateStateCount(); + getStateManager().updateStateCount(); try { JUMP_LOCK.lock(); @@ -487,7 +497,6 @@ final class M3U8VodLoader extends BaseM3U8Loader { @Override public void addComponent(IRecordHandler recordHandler) { mRecordHandler = recordHandler; - mRecord = mRecordHandler.getRecord(0); } @Override public void addComponent(IInfoTask infoTask) { @@ -543,10 +552,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { * 需要在 {@link #addComponent(IRecordHandler)}后调用 */ @Override public void addComponent(IThreadStateManager threadState) { - mManager = (VodStateManager) threadState; - mStateHandler = new Handler(mLooper, mManager.getHandlerCallback()); - mManager.setVodLoader(this); - mManager.setLooper(mRecord, mLooper); + mStateManager = threadState; } /** @@ -557,6 +563,11 @@ final class M3U8VodLoader extends BaseM3U8Loader { } + @Override + protected VodStateManager getStateManager() { + return (VodStateManager) mStateManager; + } + @Override protected void checkComponent() { if (mRecordHandler == null) { throw new NullPointerException("任务记录组件为空"); @@ -564,7 +575,7 @@ final class M3U8VodLoader extends BaseM3U8Loader { if (mInfoTask == null) { throw new NullPointerException(("文件信息组件为空")); } - if (mManager == null) { + if (getStateManager() == null) { throw new NullPointerException("任务状态管理组件为空"); } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java index 86b97bd6..b02b57a1 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodUtil.java @@ -24,7 +24,6 @@ import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.m3u8.M3U8InfoTask; import com.arialyy.aria.m3u8.M3U8Listener; -import com.arialyy.aria.m3u8.M3U8RecordHandler; import com.arialyy.aria.m3u8.M3U8TaskOption; /** @@ -54,7 +53,7 @@ public final class M3U8VodUtil extends AbsNormalLoaderUtil { @Override public LoaderStructure BuildLoaderStructure() { LoaderStructure structure = new LoaderStructure(); - structure.addComponent(new M3U8RecordHandler(getTaskWrapper())) + structure.addComponent(new VodRecordHandler(getTaskWrapper())) .addComponent(new M3U8InfoTask(getTaskWrapper())) .addComponent(new VodStateManager(getTaskWrapper(), (M3U8Listener) getListener())); structure.accept(getLoader()); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java similarity index 88% rename from M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java rename to M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java index 507dcb05..4ba82c57 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordHandler.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodRecordHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.m3u8; +package com.arialyy.aria.m3u8.vod; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.ThreadRecord; @@ -21,11 +21,12 @@ import com.arialyy.aria.core.common.RecordHandler; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.M3U8Entity; -import com.arialyy.aria.core.loader.IRecordHandler; import com.arialyy.aria.core.wrapper.ITaskWrapper; +import com.arialyy.aria.m3u8.BaseM3U8Loader; +import com.arialyy.aria.m3u8.M3U8InfoTask; +import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.FileUtil; -import com.arialyy.aria.util.RecordUtil; import java.io.File; import java.util.ArrayList; @@ -33,19 +34,15 @@ import java.util.ArrayList; * @Author lyy * @Date 2019-09-24 */ -public final class M3U8RecordHandler extends RecordHandler { +final class VodRecordHandler extends RecordHandler { private M3U8TaskOption mOption; - public M3U8RecordHandler(DTaskWrapper wrapper) { + VodRecordHandler(DTaskWrapper wrapper) { super(wrapper); - mOption = (M3U8TaskOption) wrapper.getM3u8Option(); } - @Override public void onPre() { - super.onPre(); - if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) { - RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD); - } + public void setOption(M3U8TaskOption option) { + mOption = option; } /** diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java index 2b880862..425ba1a7 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/VodStateManager.java @@ -52,7 +52,6 @@ public final class VodStateManager implements IThreadStateManager { private int cancelNum = 0; // 已经取消的线程的数 private int stopNum = 0; // 已经停止的线程数 private int failNum = 0; // 失败的线程数 - private long percent; //当前总进度,百分比进度 private long progress; private TaskRecord taskRecord; // 任务记录 private Looper looper; @@ -164,7 +163,11 @@ public final class VodStateManager implements IThreadStateManager { } break; case STATE_RUNNING: - progress += (long) msg.obj; + Bundle b = msg.getData(); + if (b != null) { + long len = b.getLong(IThreadStateManager.DATA_ADD_LEN, 0); + progress += len; + } break; } return true; @@ -226,7 +229,6 @@ public final class VodStateManager implements IThreadStateManager { int percent = completeNum * 100 / taskRecord.threadRecords.size(); getEntity().setPercent(percent); getEntity().update(); - this.percent = percent; } @Override public boolean isFail() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java index 02eb8105..4df5008b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/AbsEntity.java @@ -41,6 +41,11 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, */ @Ignore private int failNum = 0; + /** + * 剩余时间,单位:s + */ + @Ignore private int timeLeft = Integer.MAX_VALUE; + /** * 扩展字段 */ @@ -81,6 +86,18 @@ public abstract class AbsEntity extends DbEntity implements IEntity, Parcelable, */ private long stopTime = 0; + /** + * 获取剩余时间,单位:s + * 如果是m3u8任务,无法获取剩余时间;m2u8任务如果需要获取剩余时间,请设置文件长度{@link #setFileSize(long)} + */ + public int getTimeLeft() { + return timeLeft; + } + + public void setTimeLeft(int timeLeft) { + this.timeLeft = timeLeft; + } + public boolean isComplete() { return isComplete; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java index 9fe5c43a..9942ff89 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RecordHelper.java @@ -156,10 +156,18 @@ public class RecordHelper { : mTaskRecord.filePath); ThreadRecord tr = mTaskRecord.threadRecords.get(0); if (!file.exists()) { - ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); - tr.startLocation = 0; - tr.isComplete = false; - tr.endLocation = mWrapper.getEntity().getFileSize(); + // 目标文件 + File targetFile = new File(mTaskRecord.filePath); + // 处理组合任务其中一个子任务完成的情况 + if (tr.isComplete && targetFile.exists() && targetFile.length() == mWrapper.getEntity() + .getFileSize()) { + tr.isComplete = true; + } else { + ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); + tr.startLocation = 0; + tr.isComplete = false; + tr.endLocation = mWrapper.getEntity().getFileSize(); + } } else if (file.length() > mWrapper.getEntity().getFileSize()) { ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); FileUtil.deleteFile(file); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java index e7846b1b..e1b6b11d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/SubThreadConfig.java @@ -46,7 +46,7 @@ public class SubThreadConfig { public int peerIndex; // 线程任务类型 public int threadType = TYPE_HTTP; - // 更新间隔,单位:毫秒 + // 进度更新间隔,单位:毫秒 public long updateInterval = 1000; /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java index 3969971a..4dbb02c1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoader.java @@ -272,8 +272,8 @@ public abstract class AbsGroupLoader implements ILoaderVisitor, ILoader { mListener.onComplete(); return; } - startTimer(); handlerTask(looper); + startTimer(); Looper.loop(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index 6f370cf2..6e333d2a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -140,6 +140,13 @@ public abstract class BaseListener } @Override public void stop(int type) { + mUtil = createUtil(); + if (mUtil == null){ + ALog.e(TAG, "任务工具创建失败"); + return; + } isStop = true; mSchedulerType = type; getUtil().stop(); @@ -226,6 +255,11 @@ public abstract class AbsTask } @Override public void cancel(int type) { + mUtil = createUtil(); + if (mUtil == null){ + ALog.e(TAG, "任务工具创建失败"); + return; + } isCancel = true; mSchedulerType = type; getUtil().cancel(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index a9463d26..01f15423 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -67,8 +67,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { private long mRangeProgress, mLastRangeProgress; private IThreadTaskAdapter mAdapter; private ThreadRecord mRecord; - private String mThreadNmae; - private long updateInterval = 1000; // 更新间隔 + private String mThreadName; + private long updateInterval; // 更新间隔 private Thread mConfigThread = new Thread(new Runnable() { @Override public void run() { @@ -243,8 +243,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } @Override public String getThreadName() { - return mThreadNmae == null ? CommonUtil.getThreadName(getConfig().url, getThreadId()) - : mThreadNmae; + return mThreadName == null ? (mThreadName = + CommonUtil.getThreadName(getConfig().url, getThreadId())) : mThreadName; } /** @@ -287,7 +287,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { bundle.putLong(IThreadStateManager.DATA_THREAD_LOCATION, mRangeProgress); msg.what = state; int reqType = getConfig().threadType; - if (reqType == ITaskWrapper.M3U8_VOD || reqType == ITaskWrapper.M3U8_LIVE) { + if (reqType == SubThreadConfig.TYPE_M3U8_PEER) { sendM3U8Info(state, msg); } @@ -299,10 +299,10 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } private void sendM3U8Info(int state, Message msg) { + Bundle bundle = msg.getData(); if (state != IThreadStateManager.STATE_UPDATE_PROGRESS) { msg.obj = this; } - Bundle bundle = msg.getData(); if ((state == IThreadStateManager.STATE_COMPLETE || state == IThreadStateManager.STATE_FAIL)) { bundle.putString(ISchedulers.DATA_M3U8_URL, getConfig().url); bundle.putString(ISchedulers.DATA_M3U8_PEER_PATH, getConfig().tempFile.getPath()); @@ -313,6 +313,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { @Override public synchronized void updateCompleteState() { ALog.i(TAG, String.format("任务【%s】线程__%s__完成", getTaskWrapper().getKey(), mRecord.threadId)); writeConfig(true, mRecord.endLocation); + // 进度发送不是实时的,发送完成任务前,需要更新一次进度 + sendRunningState(); updateState(IThreadStateManager.STATE_COMPLETE, null); } @@ -334,17 +336,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } // 不需要太频繁发送,以减少消息队列的压力 if (System.currentTimeMillis() - mLastSendProgressTime > updateInterval) { - Message msg = mStateHandler.obtainMessage(); - Bundle b = msg.getData(); - if (b == null) { - b = new Bundle(); - msg.setData(b); - } - b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); - b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress); - msg.what = IThreadStateManager.STATE_RUNNING; - msg.obj = mRangeProgress; - msg.sendToTarget(); + sendRunningState(); mLastRangeProgress = mRangeProgress; mLastSendProgressTime = System.currentTimeMillis(); } @@ -358,6 +350,23 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver { } } + /** + * 发送执行中的数据 + */ + private void sendRunningState() { + Message msg = mStateHandler.obtainMessage(); + Bundle b = msg.getData(); + if (b == null) { + b = new Bundle(); + msg.setData(b); + } + b.putString(IThreadStateManager.DATA_THREAD_NAME, getThreadName()); + b.putLong(IThreadStateManager.DATA_ADD_LEN, mRangeProgress - mLastRangeProgress); + msg.what = IThreadStateManager.STATE_RUNNING; + msg.obj = mRangeProgress; + msg.sendToTarget(); + } + @Override public long getThreadProgress() { return mRangeProgress; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java index bbb0f4d6..5abf3cd7 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -48,6 +48,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; @@ -794,6 +795,39 @@ public class CommonUtil { return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "tb"; } + /** + * 转换时间 + * 时间<1 小时,显示分秒,显示样式 00:20 + * 时间 ≥1 小时,显示时分秒,显示样式 01:11:12 + * 时间 ≥1 天,显示天时分,显示样式 1d 01:11 + * 时间 ≥7 天,显示样式 ∞ + * + * @param seconds 单位为s的时间 + */ + public static String formatTime(int seconds) { + String standardTime; + if (seconds <= 0) { + standardTime = "00:00"; + } else if (seconds < 60) { + standardTime = String.format(Locale.getDefault(), "00:%02d", seconds % 60); + } else if (seconds < 3600) { + standardTime = String.format(Locale.getDefault(), "%02d:%02d", seconds / 60, seconds % 60); + } else if (seconds < 86400) { + standardTime = + String.format(Locale.getDefault(), "%02d:%02d:%02d", seconds / 3600, seconds % 3600 / 60, + seconds % 60); + } else if (seconds < 604800) { + standardTime = + String.format(Locale.getDefault(), "%dd %02d:%02d", seconds / 86400, + seconds % 86400 / 3600, + seconds % 3600); + } else { + standardTime = "∞"; + } + + return standardTime; + } + /** * 通过文件名获取下载配置文件路径 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 1bc3b215..30fb69fd 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -128,6 +128,7 @@ public class ComponentUtil { break; } if (className == null) { + ALog.e(TAG, "不识别的类名:" + className); return null; } T util = null; diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index ba3e6cd8..e844ab84 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -204,6 +204,7 @@ public class SingleTaskActivity extends BaseActivity { getBinding().setProgress(task.getPercent()); } getBinding().setSpeed(task.getConvertSpeed()); + getBinding().setTimeLeft(task.getConvertTimeLeft()); } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java index 48d9d47b..4a0cc8ad 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/DownloadGroupActivity.java @@ -155,10 +155,11 @@ public class DownloadGroupActivity extends BaseActivity + @@ -40,6 +44,7 @@ bind:progress="@{progress}" bind:speed="@{speed}" bind:stateStr="@{stateStr}" + bind:timeLeft="@{timeLeft}" /> +