Skip to content
This repository was archived by the owner on Oct 5, 2023. It is now read-only.

Commit

Permalink
Merge pull request #15 from opendx/0.8.6
Browse files Browse the repository at this point in the history
0.8.6
  • Loading branch information
jiangyitao authored Feb 17, 2021
2 parents 9444493 + 0e6531f commit 1749ff7
Show file tree
Hide file tree
Showing 18 changed files with 314 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test-output/
*.mp4
*.jpg
spring.log*
ext/
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.daxiang</groupId>
<artifactId>agent</artifactId>
<version>0.8.5</version>
<version>0.8.6</version>
<packaging>jar</packaging>

<properties>
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/daxiang/action/BaseAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.daxiang.core.action.annotation.Action;
import com.daxiang.core.action.annotation.Param;
import com.daxiang.core.Device;
import com.daxiang.utils.UUIDUtil;
import io.appium.java_client.MobileBy;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
Expand All @@ -12,6 +14,9 @@
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
Expand Down Expand Up @@ -58,6 +63,19 @@ public void sleep(@Param(description = "休眠时长,单位:毫秒") long ms
Thread.sleep(ms);
}

@Action(id = 3, name = "下载文件", returnValueDesc = "文件绝对路径")
public String downloadFile(@Param(description = "下载文件") String url, @Param(description = "文件扩展名,如: jpg") String ext) throws IOException {
String file = StringUtils.isEmpty(ext) ? UUIDUtil.getUUIDFilename(url) : UUIDUtil.getUUID() + "." + ext;
File downloadFile = new File(file);
FileUtils.copyURLToFile(new URL(url), downloadFile);
return downloadFile.getAbsolutePath();
}

@Action(id = 4, name = "删除文件")
public boolean deleteFileQuitely(@Param(description = "文件路径") String filePath) {
return FileUtils.deleteQuietly(new File(filePath));
}

@Action(id = 7, name = "点击")
public WebElement click(@Param(description = "查找方式", possibleValues = FIND_BY_POSSIBLE_VALUES) String findBy, String value) {
WebElement element = findElement(findBy, value);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/daxiang/action/MobileAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ public void switchContext(@Param(possibleValues = "[{'value': 'NATIVE_APP', 'des
}

@Action(id = 1001, name = "安装App", platforms = {1, 2})
public void installApp(@Param(description = "app下载地址") String appDownloadUrl) {
Assert.hasText(appDownloadUrl, "appDownloadUrl不能为空");
mobileDevice.installApp(appDownloadUrl);
public void installApp(@Param(description = "本地路径 or 下载链接") String app) {
Assert.hasText(app, "app不能为空");
mobileDevice.installApp(app);
}

@Action(id = 1002, name = "卸载App", platforms = {1, 2})
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/com/daxiang/action/YourCustomAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
*/
public class YourCustomAction {

/**
* invoke = 2 将通过YourCustomAction.randomAlphanumeric 进行调用
*/
@Action(id = 5000, name = "随机字符串(数字 & 字母)", invoke = 2)
@Action(id = 5000, name = "随机字符串(数字 & 字母)")
public static String randomAlphanumeric(@Param(description = "字符串长度") int count) {
return RandomStringUtils.randomAlphanumeric(count);
}
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/daxiang/controller/AgentExtJarController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.daxiang.controller;

import com.daxiang.model.Response;
import com.daxiang.service.AgentExtJarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Created by jiangyitao.
*/
@RestController
@RequestMapping("/agentExtJar")
public class AgentExtJarController {

@Autowired
private AgentExtJarService agentExtJarService;

@PostMapping("/load")
public Response loadExtJar(@RequestBody String jarUrl) {
agentExtJarService.loadJar(jarUrl);
return Response.success("加载成功");
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/daxiang/core/AgentStartRunner.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.daxiang.core;

import com.daxiang.core.action.BasicActionScanner;
import com.daxiang.core.classloader.AgentExtJarLoader;
import com.daxiang.core.mobile.android.ADB;
import com.daxiang.core.mobile.android.AndroidDeviceChangeListener;
import com.daxiang.core.mobile.appium.AppiumServer;
Expand Down Expand Up @@ -104,6 +105,9 @@ public void run(ApplicationArguments args) throws Exception {
List<Action> basicActions = basicActionScanner.scanRecursive(basicActionPackage);
log.info("scan: {}, basicActions: {}", basicActionPackage, basicActions);
serverClient.resetBasicAction(basicActions);

// 初始化extJars
AgentExtJarLoader.getInstance().initExtJars();
}

private void checkAppiumVersion(String appiumVersion) {
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/daxiang/core/JavaCompiler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.daxiang.core;

import com.daxiang.core.classloader.AgentExtJarLoader;
import lombok.extern.slf4j.Slf4j;
import org.dvare.dynamic.compiler.DynamicCompiler;
import org.dvare.dynamic.exceptions.DynamicCompilerException;
Expand All @@ -15,7 +16,8 @@ public static Class compile(String className, String code) throws DynamicCompile
Assert.hasLength(className, "className cannot be empty");
Assert.hasLength(code, "code cannot be empty");

DynamicCompiler dynamicCompiler = new DynamicCompiler();
ClassLoader classLoader = AgentExtJarLoader.getInstance().getClassLoader();
DynamicCompiler dynamicCompiler = new DynamicCompiler(classLoader);
dynamicCompiler.addSource(className, code);

log.info("[java编译]开始编译{}...", className);
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/daxiang/core/action/BasicActionScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -93,8 +94,9 @@ private Action createAction(String className, Method method) {
action.setDescription(actionAnno.description());
action.setType(Action.TYPE_BASE);

// 默认使用$.methodName调用,否则使用全类名.methodName调用
String actionInvoke = actionAnno.invoke() == 1 ? "$." + methodName : className + "." + methodName;
// 静态方法使用className.methodName调用,否则使用$.methodName调用
String actionInvoke = Modifier.isStatic(method.getModifiers())
? className + "." + methodName : "$." + methodName;
action.setInvoke(actionInvoke);

action.setReturnValueType(TypeUtils.toString(method.getGenericReturnType()));
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/com/daxiang/core/action/annotation/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

String description() default "";

int invoke() default 1; // 1. $.methodName 2. ClassName.methodName

int[] platforms() default {}; // 1.android 2.ios 3.web 空:所有平台通用

int state() default 2; // 0.禁用 1.草稿 2.发布
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/daxiang/core/classloader/AgentClassLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.daxiang.core.classloader;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
* Created by jiangyitao.
*/
public class AgentClassLoader extends URLClassLoader {

public AgentClassLoader() {
super(new URL[0], ClassLoader.getSystemClassLoader());
}

public void addJar(File jar) {
try {
super.addURL(jar.toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
128 changes: 128 additions & 0 deletions src/main/java/com/daxiang/core/classloader/AgentExtJarLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.daxiang.core.classloader;

import com.daxiang.model.AgentExtJar;
import com.daxiang.server.ServerClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Created by jiangyitao.
*/
@Slf4j
public class AgentExtJarLoader {

public static final String JAR_DIR = "ext";

private static final AgentExtJarLoader INSTANCE = new AgentExtJarLoader();

private Map<String, File> loadedJarMap;
private AgentClassLoader classLoader;

private AgentExtJarLoader() {
loadedJarMap = new HashMap<>();
classLoader = new AgentClassLoader();
}

public static AgentExtJarLoader getInstance() {
return INSTANCE;
}

public ClassLoader getClassLoader() {
return classLoader;
}

public synchronized void load(File jar) {
String artifactId = getJarNameWithoutVersion(jar);
if (loadedJarMap.containsKey(artifactId)) {
// 使用新的ClassLoader加载其他已经加载过的jar,否则无法做到热更新
classLoader = new AgentClassLoader();
loadedJarMap.remove(artifactId);
reload();
}

loadJar(jar);
loadedJarMap.put(artifactId, jar);
}

private String getJarNameWithoutVersion(File jar) {
Matcher matcher = Pattern.compile("(.+)-([0-9].*)\\.jar").matcher(jar.getName());
while (matcher.find()) {
return matcher.group(1); // spring-boot
}

throw new IllegalArgumentException(jar.getName() + "文件名不合法");
}

private void reload() {
log.info("reload");
loadedJarMap.values().forEach(this::loadJar);
}

private void loadJar(File jar) {
log.info("loadJar: {}", jar);
classLoader.addJar(jar);
}

/**
* 初始化ext jar
* 1. 以server返回的ext jar为准,EXT_JAR_DIR多删少下载
* 2. 加载jar
*/
public void initExtJars() {
File extJarDir = new File(JAR_DIR);
if (!extJarDir.exists()) {
extJarDir.mkdir();
}

Set<AgentExtJar> serverJars = ServerClient.getInstance().getAgentExtJars();
log.info("server extJars: {}", serverJars);

Set<AgentExtJar> localJars = Stream.of(extJarDir.listFiles()).map(file -> {
AgentExtJar localJar = new AgentExtJar();
try {
String md5 = DigestUtils.md5DigestAsHex(FileUtils.readFileToByteArray(file));
localJar.setMd5(md5);
} catch (IOException e) {
boolean deleteSuccess = file.delete();
log.error("read localJar={} err, delete it success? {}", file, deleteSuccess, e);
return null;
}
localJar.setFilename(file.getName());
localJar.setFile(file);
return localJar;
}).filter(Objects::nonNull).collect(Collectors.toSet());
log.info("local extJars: {}", localJars);

for (AgentExtJar localJar : localJars) {
if (serverJars.contains(localJar)) {
AgentExtJarLoader.getInstance().load(localJar.getFile());
} else {
// 删除和服务端不匹配的文件
boolean deleteSucess = localJar.getFile().delete();
log.info("delete local extJar: {} success? {}", localJar, deleteSucess);
}
}

// 下载本地没有的jar
serverJars.stream().filter(serverJar -> !localJars.contains(serverJar)).forEach(serverJar -> {
try {
File localJarFile = new File(JAR_DIR, serverJar.getFilename());
log.info("download {} from {}", localJarFile, serverJar.getDownloadUrl());
FileUtils.copyURLToFile(new URL(serverJar.getDownloadUrl()), localJarFile);
AgentExtJarLoader.getInstance().load(localJarFile);
} catch (IOException e) {
log.error("download {} err", serverJar.getDownloadUrl(), e);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import com.daxiang.core.mobile.android.stf.Minitouch;
import com.daxiang.core.mobile.appium.AndroidNativePageSourceHandler;
import com.daxiang.core.mobile.appium.AppiumServer;
import com.daxiang.utils.FileUtil;
import com.daxiang.utils.Terminal;
import com.daxiang.utils.UUIDUtil;
import io.appium.java_client.android.AndroidDriver;
Expand Down Expand Up @@ -141,7 +140,9 @@ public void installApp(String app) {

if (appIsUrl) {
try {
app = FileUtil.downloadFile(app).getAbsolutePath();
File appFile = new File(UUIDUtil.getUUID());
FileUtils.copyURLToFile(new URL(app), appFile);
app = appFile.getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/daxiang/model/AgentExtJar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.daxiang.model;


import lombok.Getter;
import lombok.Setter;

import java.io.File;
import java.util.Objects;

/**
* Created by jiangyitao.
*/
@Getter
@Setter
public class AgentExtJar {
private String filename;
private String md5;
private File file;
private String downloadUrl;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AgentExtJar that = (AgentExtJar) o;
return md5.equals(that.md5);
}

@Override
public int hashCode() {
return Objects.hash(md5);
}

@Override
public String toString() {
return filename;
}
}
Loading

0 comments on commit 1749ff7

Please sign in to comment.