是否可以替换Gradle用于运行Java的JavaExecution?
当类路径太长时,Java无法启动。窗户上的长度限制特别短 Gradle似乎对解决他们这边的问题不感兴趣(尽管这是他们的责任,因为他们是Java的始作俑者),所以我们最终用自己的替代方案取代了是否可以替换Gradle用于运行Java的JavaExecution?,java,gradle,Java,Gradle,当类路径太长时,Java无法启动。窗户上的长度限制特别短 Gradle似乎对解决他们这边的问题不感兴趣(尽管这是他们的责任,因为他们是Java的始作俑者),所以我们最终用自己的替代方案取代了JavaExec任务 替代方案如下所示: public class WorkingJavaExec extends JavaExec { private static final String MATCH_CHUNKS_OF_70_CHARACTERS = "(?<=\\G.{7
JavaExec
任务
替代方案如下所示:
public class WorkingJavaExec extends JavaExec {
private static final String MATCH_CHUNKS_OF_70_CHARACTERS =
"(?<=\\G.{70})";
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void exec() {
FileCollection oldClasspath = getClasspath();
File jarFile = null;
try {
if (!oldClasspath.isEmpty()) {
try {
jarFile =
toJarWithClasspath(oldClasspath.getFiles());
setClasspath(getProject().files(jarFile));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
super.exec();
} finally {
setClasspath(oldClasspath);
if (jarFile != null) {
try {
Files.delete(jarFile.toPath());
} catch (Exception e) {
logger.warn("Couldn't delete: " + jarFile, e);
}
}
}
}
public static File toJarWithClasspath(Set<File> files)
throws IOException {
File jarFile = File.createTempFile("long-classpath", ".jar");
try (ZipOutputStream zip =
new ZipOutputStream(new FileOutputStream(jarFile))) {
zip.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
try (PrintWriter writer =
new PrintWriter(
new OutputStreamWriter(
zip, StandardCharsets.UTF_8))) {
writer.println("Manifest-Version: 1.0");
String classPath = files.stream().map(
file -> file.toURI().toString())
.collect(Collectors.joining(" "));
String classPathEntry = "Class-Path: " + classPath;
writer.println(Arrays.stream(
classPathEntry.split(MATCH_CHUNKS_OF_70_CHARACTERS))
.collect(Collectors.joining("\n ")));
}
}
return jarFile;
}
}
public类WorkingJavaExec扩展了JavaExec{
私有静态最终字符串匹配\u块\u个字符=
(?您可以使用jar
任务将类路径添加到清单中:
jar {
baseName = "my-app"
version = "1.0.0"
manifest {
attributes("Class-Path": configurations.compile.collect { it.getName() }.join(' '))
}
}
然后,您可以在启动时引用该jar:
task run(type:JavaExec) {
classpath = jar.outputs.files
main = "myapp.MainClass"
}
这可以绕过命令行路径限制。您可能还希望将依赖项jar复制到输出文件夹,以便它们在运行时可用
task copyDependencies(type: Copy, dependsOn: [ "build" ]) {
from configurations.runtime
into "./build/libs"
}
build.finalizedBy(copyDependencies)
有用吗?我不确定您是否可以“替换”JavaExecAction
,因为它是在JavaExec任务实例化期间设置的,但我认为您可以使用自定义的插件来更好地解决此问题,如下所示:
class FixClasspathLimitPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
// after project has been evaluated, hack into all tasks of type JavaExec declared.
project.afterEvaluate {
project.tasks.stream().filter { task -> task instanceof JavaExec }.forEach {
println "Reconfiguring classpath for : $it"
JavaExec javaExec = (JavaExec) it;
FileCollection oldClasspath = javaExec.getClasspath()
// insert an Action at first position, that will change classpath
javaExec.doFirst { task ->
((JavaExec) task).setClasspath(getProject().files(toJarWithClasspath(oldClasspath.getFiles())));
}
// optional - reset old classpath
javaExec.doLast { task ->
((JavaExec) task).setClasspath(oldClasspath)
}
}
}
}
public static File toJarWithClasspath(Set<File> files)
throws Exception {
// same method implementation as given in your question
}
类FixClasspathLimitPlugin实现插件{
@凌驾
无效申请(项目){
//在对项目进行评估之后,侵入所有JavaExec类型的任务。
项目后评估{
project.tasks.stream().filter{task->task instanceof JavaExec}.forEach{
println“为$it重新配置类路径”
JavaExec-JavaExec=(JavaExec)it;
FileCollection oldClasspath=javaExec.getClasspath()
//在第一个位置插入将更改类路径的操作
javaExec.doFirst{task->
((JavaExec)task).setClasspath(getProject().files(toJarWithClasspath(oldclaspath.getFiles()));
}
//可选-重置旧类路径
javaExec.doLast{task->
((JavaExec)任务).setClasspath(oldClasspath)
}
}
}
}
公共静态文件toJarWithClasspath(设置文件)
抛出异常{
//与问题中给出的方法实现相同
}
这样,您就不必在团队编写的所有构建脚本中替换JavaExec,您只需确保这些脚本应用您的插件即可
如果您使用Gradle的自定义发行版并在企业中使用包装器,您甚至可以将此插件作为初始脚本包含在此发行版中,如下所述:
在gradle发行版的gradle_HOME/init.d/目录中放置一个以.gradle结尾的文件。这允许您打包一个包含一些自定义生成逻辑和插件的自定义gradle发行版。您可以将其与gradle包装器结合,以使自定义逻辑可用于企业中的所有生成
这样,插件将以“透明”的方式应用
关于Test
任务:我认为它不使用JavaExecAction
,但是可以应用类似的解决方案,使用类似的插件。作为一个插件,我认为这肯定有一些优点,因为它减少了人们在包含解决方法时所需的工作量。它比just JavaExec,因为使用JavaExecAction的所有其他任务也需要更改,而且我不完全确定如何找出行为不端任务的完整列表:(另外,我认为如果我们要制作一个定制的Gradle发行版,最好的解决方案可能是修改JavaExecution本身,使其在不修改的情况下正常工作。我认为我将避免修改jar本身内部的类路径,因为一旦项目打包,路径将再次出错,而我在过去注意到即使您没有使用java-jar
,JVM也会尝试解析类路径上的jar,这会减慢启动速度并产生不必要的警告。因此,如果我们制作一个单独的jar,并在其中包含解决方法,以避免修改最终分发的主jar,这种方法将最有效。