/* * 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 org.apache.tools.ant.taskdefs.rmic; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Vector; import java.util.stream.Collectors; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Rmic; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.util.StringUtils; /** * This is the default implementation for the RmicAdapter interface. * Currently, this is a cut-and-paste of the original rmic task and * DefaultCompilerAdapter. * * @since Ant 1.4 */ public abstract class DefaultRmicAdapter implements RmicAdapter { private static final Random RAND = new Random(); /** suffix denoting a stub file: {@value} */ public static final String RMI_STUB_SUFFIX = "_Stub"; /** suffix denoting a skel file: {@value} */ public static final String RMI_SKEL_SUFFIX = "_Skel"; /** suffix denoting a tie file: {@value} */ public static final String RMI_TIE_SUFFIX = "_Tie"; /** arg for compat: {@value} */ public static final String STUB_COMPAT = "-vcompat"; /** arg for 1.1: {@value} */ public static final String STUB_1_1 = "-v1.1"; /** arg for 1.2: {@value} */ public static final String STUB_1_2 = "-v1.2"; /** * option for stub 1.1 in the rmic task: {@value} */ public static final String STUB_OPTION_1_1 = "1.1"; /** * option for stub 1.2 in the rmic task: {@value} */ public static final String STUB_OPTION_1_2 = "1.2"; /** * option for stub compat in the rmic task: {@value} */ public static final String STUB_OPTION_COMPAT = "compat"; private Rmic attributes; private FileNameMapper mapper; /** * Sets Rmic attributes * @param attributes the rmic attributes */ @Override public void setRmic(final Rmic attributes) { this.attributes = attributes; mapper = new RmicFileNameMapper(); } /** * Get the Rmic attributes * @return the attributes as a Rmic taskdef */ public Rmic getRmic() { return attributes; } /** * Gets the stub class suffix * @return the stub suffix "_Stub" */ protected String getStubClassSuffix() { return RMI_STUB_SUFFIX; } /** * Gets the skeleton class suffix * @return the skeleton suffix "_Skel" */ protected String getSkelClassSuffix() { return RMI_SKEL_SUFFIX; } /** * Gets the tie class suffix * @return the tie suffix "_Tie" */ protected String getTieClassSuffix() { return RMI_TIE_SUFFIX; } /** * This implementation returns a mapper that may return up to two * file names. * * <ul> * <li>for JRMP it will return *_getStubClassSuffix (and * *_getSkelClassSuffix if JDK 1.1 is used)</li> * * <li>for IDL it will return a random name, causing <rmic> to * always recompile.</li> * * <li>for IIOP it will return _*_getStubClassSuffix for * interfaces and _*_getStubClassSuffix for non-interfaces (and * determine the interface and create _*_Stub from that).</li> * </ul> * @return a <code>FileNameMapper</code> */ @Override public FileNameMapper getMapper() { return mapper; } /** * Gets the CLASSPATH this rmic process will use. * @return the classpath */ @Override public Path getClasspath() { return getCompileClasspath(); } /** * Builds the compilation classpath. * @return the classpath */ protected Path getCompileClasspath() { Path classpath = new Path(attributes.getProject()); // add dest dir to classpath so that previously compiled and // untouched classes are on classpath classpath.setLocation(attributes.getBase()); // Combine the build classpath with the system classpath, in an // order determined by the value of build.sysclasspath Path cp = attributes.getClasspath(); if (cp == null) { cp = new Path(attributes.getProject()); } if (attributes.getIncludeantruntime()) { classpath.addExisting(cp.concatSystemClasspath("last")); } else { classpath.addExisting(cp.concatSystemClasspath("ignore")); } if (attributes.getIncludejavaruntime()) { classpath.addJavaRuntime(); } return classpath; } /** * Setup rmic argument for rmic. * @return the command line */ protected Commandline setupRmicCommand() { return setupRmicCommand(null); } /** * Setup rmic argument for rmic. * @param options additional parameters needed by a specific * implementation. * @return the command line */ protected Commandline setupRmicCommand(String[] options) { Commandline cmd = new Commandline(); if (options != null) { for (int i = 0; i < options.length; i++) { cmd.createArgument().setValue(options[i]); } } Path classpath = getCompileClasspath(); cmd.createArgument().setValue("-d"); cmd.createArgument().setFile(attributes.getOutputDir()); if (attributes.getExtdirs() != null) { cmd.createArgument().setValue("-extdirs"); cmd.createArgument().setPath(attributes.getExtdirs()); } cmd.createArgument().setValue("-classpath"); cmd.createArgument().setPath(classpath); String stubOption = addStubVersionOptions(); if (stubOption != null) { //set the non-null stubOption cmd.createArgument().setValue(stubOption); } if (null != attributes.getSourceBase()) { cmd.createArgument().setValue("-keepgenerated"); } if (attributes.getIiop()) { attributes.log("IIOP has been turned on.", Project.MSG_INFO); cmd.createArgument().setValue("-iiop"); if (attributes.getIiopopts() != null) { attributes.log("IIOP Options: " + attributes.getIiopopts(), Project.MSG_INFO); cmd.createArgument().setValue(attributes.getIiopopts()); } } if (attributes.getIdl()) { cmd.createArgument().setValue("-idl"); attributes.log("IDL has been turned on.", Project.MSG_INFO); if (attributes.getIdlopts() != null) { cmd.createArgument().setValue(attributes.getIdlopts()); attributes.log("IDL Options: " + attributes.getIdlopts(), Project.MSG_INFO); } } if (attributes.getDebug()) { cmd.createArgument().setValue("-g"); } String[] compilerArgs = attributes.getCurrentCompilerArgs(); compilerArgs = preprocessCompilerArgs(compilerArgs); cmd.addArguments(compilerArgs); verifyArguments(cmd); logAndAddFilesToCompile(cmd); return cmd; } /** * This is an override point; get the stub version off the rmic command and * translate that into a compiler-specific argument * @return a string to use for the stub version; can be null * @since Ant1.7.1 */ protected String addStubVersionOptions() { //handle the many different stub options. String stubVersion = attributes.getStubVersion(); //default is compatibility String stubOption = null; if (null != stubVersion) { if (STUB_OPTION_1_1.equals(stubVersion)) { stubOption = STUB_1_1; } else if (STUB_OPTION_1_2.equals(stubVersion)) { stubOption = STUB_1_2; } else if (STUB_OPTION_COMPAT.equals(stubVersion)) { stubOption = STUB_COMPAT; } else { //anything else attributes.log("Unknown stub option " + stubVersion); //do nothing with the value? or go -v+stubVersion?? } } //for java1.5+, we generate compatible stubs, that is, unless //the caller asked for IDL or IIOP support. if (stubOption == null && !attributes.getIiop() && !attributes.getIdl()) { stubOption = STUB_COMPAT; } return stubOption; } /** * Preprocess the compiler arguments in any way you see fit. * This is to allow compiler adapters to validate or filter the arguments. * The base implementation returns the original compiler arguments unchanged. * @param compilerArgs the original compiler arguments * @return the filtered set. */ protected String[] preprocessCompilerArgs(String[] compilerArgs) { return compilerArgs; } /** * Strip out all -J args from the command list. Invoke this from * {@link #preprocessCompilerArgs(String[])} if you have a non-forking * compiler. * @param compilerArgs the original compiler arguments * @return the filtered set. */ protected String[] filterJvmCompilerArgs(String[] compilerArgs) { int len = compilerArgs.length; List<String> args = new ArrayList<>(len); for (int i = 0; i < len; i++) { String arg = compilerArgs[i]; if (arg.startsWith("-J")) { attributes.log("Dropping " + arg + " from compiler arguments"); } else { args.add(arg); } } return args.toArray(new String[args.size()]); } /** * Logs the compilation parameters, adds the files to compile and logs the * "niceSourceList" * @param cmd the commandline args */ protected void logAndAddFilesToCompile(Commandline cmd) { Vector<String> compileList = attributes.getCompileList(); attributes.log("Compilation " + cmd.describeArguments(), Project.MSG_VERBOSE); StringBuilder niceSourceList = new StringBuilder(compileList.size() == 1 ? "File" : "Files") .append(" to be compiled:"); niceSourceList.append( compileList.stream().peek(arg -> cmd.createArgument().setValue(arg)) .collect(Collectors.joining(" "))); attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE); } private void verifyArguments(Commandline cmd) { if (JavaEnvUtils.isAtLeastJavaVersion(JavaEnvUtils.JAVA_9)) { for (String arg : cmd.getArguments()) { if ("-Xnew".equals(arg)) { throw new BuildException("JDK9 has removed support for -Xnew"); } } } } /** * Mapper that may return up to two file names. * * <ul> * <li>for JRMP it will return *_getStubClassSuffix (and * *_getSkelClassSuffix if JDK 1.1 is used)</li> * * <li>for IDL it will return a random name, causing <rmic> to * always recompile.</li> * * <li>for IIOP it will return _*_getStubClassSuffix for * interfaces and _*_getStubClassSuffix for non-interfaces (and * determine the interface and create _*_Stub from that).</li> * </ul> */ private class RmicFileNameMapper implements FileNameMapper { /** * Empty implementation. */ @Override public void setFrom(String s) { } /** * Empty implementation. */ @Override public void setTo(String s) { } @Override public String[] mapFileName(String name) { if (name == null || !name.endsWith(".class") || name.endsWith(getStubClassSuffix() + ".class") || name.endsWith(getSkelClassSuffix() + ".class") || name.endsWith(getTieClassSuffix() + ".class")) { // Not a .class file or the one we'd generate return null; } // we know that name.endsWith(".class") String base = StringUtils.removeSuffix(name, ".class"); String classname = base.replace(File.separatorChar, '.'); if (attributes.getVerify() && !attributes.isValidRmiRemote(classname)) { return null; } /* * fallback in case we have trouble loading the class or * don't know how to handle it (there is no easy way to * know what IDL mode would generate. * * This is supposed to make Ant always recompile the * class, as a file of that name should not exist. */ String[] target = new String[] { name + ".tmp." + RAND.nextLong() }; if (!attributes.getIiop() && !attributes.getIdl()) { // JRMP with simple naming convention if (STUB_OPTION_1_2.equals(attributes.getStubVersion())) { target = new String[] { base + getStubClassSuffix() + ".class" }; } else { target = new String[] { base + getStubClassSuffix() + ".class", base + getSkelClassSuffix() + ".class", }; } } else if (!attributes.getIdl()) { int lastSlash = base.lastIndexOf(File.separatorChar); String dirname; /* * I know, this is not necessary, but I prefer it explicit (SB) */ int index = -1; if (lastSlash == -1) { // no package index = 0; dirname = ""; } else { index = lastSlash + 1; dirname = base.substring(0, index); } String filename = base.substring(index); try { Class<?> c = attributes.getLoader().loadClass(classname); if (c.isInterface()) { // only stub, no tie target = new String[] { dirname + "_" + filename + getStubClassSuffix() + ".class" }; } else { /* * stub is derived from implementation, * tie from interface name. */ Class<?> interf = attributes.getRemoteInterface(c); String iName = interf.getName(); String iDir; int iIndex; int lastDot = iName.lastIndexOf('.'); if (lastDot == -1) { // no package iIndex = 0; iDir = ""; } else { iIndex = lastDot + 1; iDir = iName.substring(0, iIndex); iDir = iDir.replace('.', File.separatorChar); } target = new String[] { dirname + "_" + filename + getTieClassSuffix() + ".class", iDir + "_" + iName.substring(iIndex) + getStubClassSuffix() + ".class" }; } } catch (ClassNotFoundException e) { attributes.log("Unable to verify class " + classname + ". It could not be found.", Project.MSG_WARN); } catch (NoClassDefFoundError e) { attributes.log("Unable to verify class " + classname + ". It is not defined.", Project.MSG_WARN); } catch (Throwable t) { attributes.log("Unable to verify class " + classname + ". Loading caused Exception: " + t.getMessage(), Project.MSG_WARN); } } return target; } } }