View Javadoc
1   /**
2    * Copyright (C) 2008-2010 Matt Gumbley, DevZendo.org <http://devzendo.org>;
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /**
18   * 
19   */
20  package org.devzendo.xplp;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.Properties;
25  import java.util.Set;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.plugin.AbstractMojo;
29  
30  
31  /**
32   * Creates a Mac OS X launcher directory structure.
33   * See
34   * http://developer.apple.com/documentation/Java/Conceptual/Java14Development/03-JavaDeployment/JavaDeployment.html
35   * for details.
36   * 
37   * @author matt
38   *
39   */
40  public final class MacOSXAppLauncherCreator extends LauncherCreator {
41      private static final String LINE_SEPARATOR = System.getProperty("line.separator");
42      public static final String APPLE_STUB = "Apple";
43      public static final String UNIVERSAL_STUB = "Universal";
44      private final String mFileType;
45      private final String mIconsFileName;
46      private final String mBundleSignature;
47      private final String mBundleOsType;
48      private final String mBundleTypeName;
49      private final String mLauncherType;
50      private final String mStubType;
51  
52      /**
53       * @param mojo the parent mojo class
54       * @param outputDirectory where to create the .app structure 
55       * @param mainClassName the main class
56       * @param applicationName the name of the application
57       * @param libraryDirectory where the libraries are stored
58       * @param transitiveArtifacts the set of transitive artifact dependencies
59       * @param resourceDirectories the project's resource directories
60       * @param parameterProperties the plugin configuration parameters, as properties
61       * @param systemProperties an array of name=value system properties
62       * @param vmArguments an array of arguments to the VM
63       * @param narClassifierTypes an array of NAR classifier:types
64       * @param launcherType the launcher type, Console or GUI
65       * @param fileType the file type (currently unused)
66       * @param iconsFileName the name of the icons file
67       * @param bundleSignature the bundle signature
68       * @param bundleOsType the bundle OS type
69       * @param bundleTypeName the bundle type name
70       * @param stubType the stub type, Apple or Universal
71       */
72      public MacOSXAppLauncherCreator(final AbstractMojo mojo,
73              final File outputDirectory,
74              final String mainClassName,
75              final String applicationName,
76              final String libraryDirectory,
77              final Set<Artifact> transitiveArtifacts,
78              final Set<File> resourceDirectories,
79              final Properties parameterProperties,
80              final String[] systemProperties,
81              final String[] vmArguments,
82              final String[] narClassifierTypes,
83              final String launcherType,
84              final String fileType,
85              final String iconsFileName,
86              final String bundleSignature,
87              final String bundleOsType,
88              final String bundleTypeName,
89              final String stubType) {
90          super(mojo, outputDirectory, mainClassName,
91              applicationName, libraryDirectory,
92              transitiveArtifacts, resourceDirectories,
93              parameterProperties, systemProperties, vmArguments,
94              narClassifierTypes);
95          mFileType = fileType;
96          mIconsFileName = iconsFileName;
97          mBundleSignature = bundleSignature;
98          mBundleOsType = bundleOsType;
99          mBundleTypeName = bundleTypeName;
100         mLauncherType = launcherType;
101         mStubType = stubType;
102     }
103 
104     private void validate() {
105         if (mIconsFileName == null || mIconsFileName.length() == 0) {
106             final String message = "No iconsFileName specified - this is mandatory for Mac OS X";
107             getMojo().getLog().warn(message);
108             throw new IllegalStateException(message);
109         }
110         if (mBundleSignature == null || mBundleSignature.length() == 0) {
111             final String message = "No bundleSignature specified - this is mandatory for Mac OS X";
112             getMojo().getLog().warn(message);
113             throw new IllegalStateException(message);
114         }
115         if (mStubType == null || ((!mStubType.equals(APPLE_STUB)) && (!mStubType.equals(UNIVERSAL_STUB))) ) {
116             final String message = "Stub type must be '" + APPLE_STUB + "' or '" + UNIVERSAL_STUB + "'";
117             getMojo().getLog().warn(message);
118             throw new IllegalStateException(message);
119         }
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public void createLauncher() throws IOException {
127         validate();
128         getParameterProperties().put("xplp.macosxsystemproperties", systemPropertiesAsPlistDict(getSystemProperties()));
129         getParameterProperties().put("xplp.macosxvmoptionsarray", vmArgumentsAsPlistArray(getVmArguments()));
130         getParameterProperties().put("xplp.macosxclasspatharray", transitiveArtifactsAsPlistArray(getTransitiveArtifacts()));
131 
132         getMojo().getLog().info("Launcher type:               " + mLauncherType);
133         getMojo().getLog().info("Icons file name:             " + mIconsFileName);
134         getMojo().getLog().info("File type:                   " + mFileType);
135         getMojo().getLog().info("Bundle signature:            " + mBundleSignature);
136         getMojo().getLog().info("Bundle OS type:              " + mBundleOsType);
137         getMojo().getLog().info("Bundle type name:            " + mBundleTypeName);
138         getMojo().getLog().info("Stub type:                   " + mStubType);
139 
140         final File osOutputDir = new File(getOutputDirectory(), "macosx");
141         final File appDir = new File(osOutputDir, getApplicationName() + ".app");
142         final File contentsDir = new File(appDir, "Contents");
143         final File macOSDir = new File(contentsDir, "MacOS");
144         final File resourcesDir = new File(contentsDir, "Resources");
145         final File libDir = new File(resourcesDir, "Java/lib");
146         osOutputDir.mkdirs();
147         appDir.mkdirs();
148         contentsDir.mkdirs();
149         macOSDir.mkdirs();
150         resourcesDir.mkdirs();
151         libDir.mkdirs();
152         final boolean allDirsOK = osOutputDir.exists()
153             && appDir.exists() && contentsDir.exists()
154             && macOSDir.exists() && resourcesDir.exists() 
155             && libDir.exists();
156         if (!allDirsOK) {
157             throw new IOException("Could not create required directories under " + appDir.getAbsolutePath());
158         }
159         
160         final File javaApplicationStub = new File(macOSDir, "JavaApplicationStub");
161         if (mStubType.equals(APPLE_STUB)) {
162             copyPluginResource("macosx/JavaApplicationStub", javaApplicationStub);
163         }
164         if (mStubType.equals(UNIVERSAL_STUB)) {
165             copyPluginResource("macosx/universalJavaApplicationStub", javaApplicationStub);
166         }
167         makeExecutable(javaApplicationStub);
168         
169         copyProjectResource(mIconsFileName, new File(resourcesDir, mIconsFileName));
170         
171         copyInterpolatedPluginResource("macosx/Info.plist", new File(contentsDir, "Info.plist"));
172         copyInterpolatedPluginResource("macosx/PkgInfo", new File(contentsDir, "PkgInfo"));
173         
174         copyTransitiveArtifacts(libDir);
175     }
176 
177     private String vmArgumentsAsPlistArray(final String[] vmArguments) {
178         final StringBuilder argsAsArray = new StringBuilder();
179         if (vmArguments.length > 0) {
180             argsAsArray.append("        <key>VMOptions</key>");
181             argsAsArray.append(LINE_SEPARATOR);
182             argsAsArray.append("        <array>");
183             argsAsArray.append(LINE_SEPARATOR);
184             for (final String vmArg : vmArguments) {
185                 argsAsArray.append("           <string>");
186                 argsAsArray.append(vmArg);
187                 argsAsArray.append("</string>");
188                 argsAsArray.append(LINE_SEPARATOR);
189             }
190             argsAsArray.append("        </array>");
191             argsAsArray.append(LINE_SEPARATOR);
192         }
193         return argsAsArray.toString();
194     }
195 
196     private String systemPropertiesAsPlistDict(final String[] systemProperties) {
197         final StringBuilder propsAsDictEntries = new StringBuilder();
198         for (int i = 0; i < systemProperties.length; i++) {
199             final String prop = systemProperties[i];
200             appendSystemProperty(propsAsDictEntries, prop);
201             propsAsDictEntries.append(LINE_SEPARATOR);
202         }
203         return propsAsDictEntries.toString();
204     }
205 
206     private void appendSystemProperty(
207             final StringBuilder propsAsDictEntries,
208             final String prop) {
209         final String[] split = prop.split("\\s*=\\s*");
210         propsAsDictEntries.append("            <key>");
211         propsAsDictEntries.append(split[0]);
212         propsAsDictEntries.append("</key>");
213         propsAsDictEntries.append(LINE_SEPARATOR);
214         propsAsDictEntries.append("            <string>");
215         propsAsDictEntries.append(split[1]);
216         propsAsDictEntries.append("</string>");
217     }
218 
219     private String transitiveArtifactsAsPlistArray(final Set<Artifact> transitiveArtifacts) {
220         final StringBuilder libsAsArtifacts = new StringBuilder();
221         final Set<String> transitiveArtifactFileNames = getTransitiveJarOrNarArtifactFileNames(transitiveArtifacts);
222         for (final String fileName : transitiveArtifactFileNames) {
223             libsAsArtifacts.append("            <string>$JAVAROOT/lib/");
224             libsAsArtifacts.append(fileName);
225             libsAsArtifacts.append("</string>");
226             libsAsArtifacts.append(LINE_SEPARATOR);
227         }
228 
229         return libsAsArtifacts.toString();
230     }
231 }