View Javadoc
1   package org.codehaus.mojo.rmic;
2   
3   /*
4    * Copyright (c) 2005-2017, Codehaus.org
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy of
7    * this software and associated documentation files (the "Software"), to deal in
8    * the Software without restriction, including without limitation the rights to
9    * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10   * of the Software, and to permit persons to whom the Software is furnished to do
11   * so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  
25  import org.apache.maven.plugin.logging.Log;
26  import org.codehaus.plexus.compiler.CompilerException;
27  import org.codehaus.plexus.util.StringUtils;
28  
29  import java.io.File;
30  import java.io.OutputStream;
31  import java.lang.reflect.Constructor;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.net.URL;
35  import java.net.URLClassLoader;
36  import java.util.ArrayList;
37  import java.util.List;
38  
39  /**
40   * A base class for invocation of rmi compilers whose arguments match those required by the JDK version of rmic.
41   */
42  abstract class AbstractRmiCompiler implements RmiCompiler
43  {
44      private Log logger;
45  
46      /* A facade to enable unit testing to control compiler access. */
47      private static ClassLoaderFacade classLoaderFacade = new ClassLoaderFacadeImpl();
48  
49      public void setLog( Log log )
50      {
51          logger = log;
52      }
53  
54      public Log getLog()
55      {
56          return logger;
57      }
58  
59      /**
60       * Returns the object to use for classloading.
61       * @return the appropriate loader facade
62       */
63      static ClassLoaderFacade getClassLoaderFacade()
64      {
65          return classLoaderFacade;
66      }
67  
68      /**
69       * Execute the compiler
70       *
71       * @param rmiConfig The config object
72       * @throws RmiCompilerException if there is a problem during compile
73       */
74      public void execute( RmiCompilerConfiguration rmiConfig )
75          throws RmiCompilerException
76      {
77          // ----------------------------------------------------------------------
78          // Build the argument list
79          // ----------------------------------------------------------------------
80  
81          List<String> arguments = new ArrayList<>();
82  
83          List<String> classpathList = rmiConfig.getClasspathEntries();
84          if ( classpathList.size() > 0 )
85          {
86              arguments.add( "-classpath" );
87              arguments.add( buildClasspath( classpathList ) );
88          }
89  
90          arguments.add( "-d" );
91          arguments.add( rmiConfig.getOutputLocation() );
92  
93          if ( rmiConfig.getVersion() != null )
94          {
95              arguments.add( "-v" + rmiConfig.getVersion() );
96          }
97  
98          if ( rmiConfig.isIiop() )
99          {
100             arguments.add( "-iiop" );
101 
102             if ( rmiConfig.isPoa() )
103             {
104                 arguments.add( "-poa" );
105             }
106             if ( rmiConfig.isNoLocalStubs() )
107             {
108                 arguments.add( "-nolocalstubs" );
109             }
110         }
111         else
112         {
113             if ( rmiConfig.isPoa() )
114             {
115                 throw new RmiCompilerException( "IIOP must be enabled in order to use the POA option" );
116             }
117 
118         }
119 
120         if ( rmiConfig.isIdl() )
121         {
122             arguments.add( "-idl" );
123             if ( rmiConfig.isNoValueMethods() )
124             {
125                 arguments.add( "-noValueMethods" );
126             }
127         }
128 
129         if ( rmiConfig.isKeep() )
130         {
131             arguments.add( "-keep" );
132         }
133 
134         if ( getLog().isDebugEnabled() || rmiConfig.isVerbose() )
135         {
136             arguments.add( "-verbose" );
137         }
138         else if ( rmiConfig.isNowarn() )
139         {
140             arguments.add( "-nowarn" );
141         }
142 
143         for ( File remoteClass : rmiConfig.getSourceFiles() )
144         {
145             arguments.add( fileToClassName( remoteClass.getPath() ) );
146         }
147 
148         String[] args = arguments.toArray( new String[arguments.size()] );
149 
150         if ( getLog().isDebugEnabled() )
151         {
152             getLog().debug( "rmic arguments: " );
153 
154             for ( String arg : args )
155             {
156                 getLog().debug( arg );
157             }
158         }
159 
160         try
161         {
162             compileInProcess( args );
163         }
164         catch ( CompilerException e )
165         {
166             throw new RmiCompilerException( e.getMessage(), e );
167         }
168     }
169 
170     private String buildClasspath( List<String> classpathList )
171     {
172         StringBuilder classpath = new StringBuilder( classpathList.get( 0 ) );
173         for ( int i = 1; i < classpathList.size(); i++ )
174         {
175             classpath.append( File.pathSeparator ).append( classpathList.get( i ) );
176         }
177 
178         return classpath.toString();
179     }
180 
181     private static String fileToClassName( String classFileName )
182     {
183         return StringUtils.replace( StringUtils.replace( classFileName, ".class", "" ), File.separator, "." );
184     }
185 
186     protected void compileInProcess( String[] args )
187         throws CompilerException
188     {
189         final Class<?> mainClass = createMainClass();
190         final Thread thread = Thread.currentThread();
191         final ClassLoader contextClassLoader = thread.getContextClassLoader();
192         thread.setContextClassLoader( mainClass.getClassLoader() );
193         try
194         {
195             compileInProcess0( mainClass, args );
196         }
197         finally
198         {
199             thread.setContextClassLoader( contextClassLoader );
200         }
201     }
202 
203     protected abstract Class<?> createMainClass()
204         throws CompilerException;
205 
206     private static void compileInProcess0( Class<?> rmicMainClass, String[] args )
207         throws CompilerException
208     {
209         try
210         {
211             Constructor<?> constructor = rmicMainClass.getConstructor( OutputStream.class, String.class );
212 
213             Object main = constructor.newInstance( System.out, "rmic" );
214 
215             Method compile = rmicMainClass.getMethod( "compile", String[].class );
216 
217             compile.invoke( main, new Object[] { args } );
218         }
219         catch ( NoSuchMethodException | IllegalAccessException | IllegalArgumentException
220                 | InstantiationException | InvocationTargetException e )
221         {
222             throw new CompilerException( "Error while executing the compiler.", e );
223         }
224     }
225 
226     /**
227      * An interface for loading the proper RMI compiler class.
228      */
229     interface ClassLoaderFacade
230     {
231         /**
232          * Updates the active classloader to include the specified URLs before the original definitions.
233          *
234          * @param urls a list of URLs to include when searching for classes.
235          */
236         void prependUrls( URL... urls );
237 
238         /**
239          * Loads the specified class using the appropriate classloader.
240          *
241          * @param rmiCompilerClass the name of the class to use for post-processing Java classes for use with Iiop.
242          * @throws ClassNotFoundException if the specified class doesn't exist
243          * @return the actual compiler class to use
244          */
245         Class<?> loadClass( String rmiCompilerClass ) throws ClassNotFoundException;
246     }
247 
248     /**
249      * The implementation of ClassLoaderFacade used at runtime.
250      */
251     private static class ClassLoaderFacadeImpl implements ClassLoaderFacade
252     {
253         ClassLoader classLoader = getClass().getClassLoader();
254 
255         public void prependUrls( URL... urls )
256         {
257             classLoader = new URLClassLoader( urls, classLoader );
258         }
259 
260         public Class<?> loadClass( String rmiCompilerClass ) throws ClassNotFoundException
261         {
262             return classLoader.loadClass( rmiCompilerClass );
263         }
264 
265     }
266 }