source: release-kits/shared/ant-installer/src/org/tp23/antinstaller/runtime/exe/PropertyLoaderFilter.java@ 15210

Last change on this file since 15210 was 15210, checked in by oranfry, 16 years ago

Lots of changes to the installer. Now only look in LanguagePack resource bundle for strings.

File size: 16.7 KB
Line 
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limiations under the License.
13 */
14
15package org.tp23.antinstaller.runtime.exe;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Properties;
26import java.util.ResourceBundle;
27import java.util.StringTokenizer;
28
29import org.tp23.antinstaller.InstallException;
30import org.tp23.antinstaller.Installer;
31import org.tp23.antinstaller.InstallerContext;
32import org.tp23.antinstaller.PropertiesFileRenderer;
33import org.tp23.antinstaller.input.ConditionalField;
34import org.tp23.antinstaller.input.InputField;
35import org.tp23.antinstaller.input.OutputField;
36import org.tp23.antinstaller.input.PasswordTextInput;
37import org.tp23.antinstaller.input.TargetInput;
38import org.tp23.antinstaller.input.TargetSelectInput;
39import org.tp23.antinstaller.page.Page;
40import org.tp23.antinstaller.runtime.VersionHelper;
41import org.tp23.antinstaller.runtime.exe.ExecuteRunnerFilter.AbortException;
42
43
44/**
45 * Loads properties from a file of default properties if found.
46 * the Installer element should define an attribute
47 * loadDefaults with one of the following values.
48 * <li>false - do not look for defaults</li>
49 * <li>prompt - look for properties and ask if they should be used if found</li>
50 * <li>load - look for defaults if found load them</li>
51 * <li>prompt-auto - wierd case where installer permits zero user interaction running only from antinstaller-config.xml defaults</li>
52 *
53 * N.B. this is not a generic property loader but one specifically for properties files
54 * generated by a previous run of an identical installer or one that according to the version
55 * number is compatible, see PropertyTask for loading other property sets
56 * FIXME i18n for AbortExceptoins
57 * @author teknopaul
58 *
59 */
60public class PropertyLoaderFilter implements ExecuteFilter {
61
62 private static final ResourceBundle res = ResourceBundle.getBundle("resources.LanguagePack");
63
64 public static final String LOAD = "true";
65 public static final String PROMPT = "prompt";
66 public static final String PROMPT_AUTO = "prompt-auto";
67 public static final String FALSE = "false";
68 public static final String DEFAULT_PROPERTIES_FILE_PROPERTY = "antinstaller.properties";
69
70 private final String fileNameProperty;
71
72 private int definedPropertiesCount;
73
74 /**
75 * Default constructor required for an ExecuteFilter implementation.
76 * The default property name given by @see{DEFAULT_PROPERTIES_FILE_PROPERTY}
77 * is used with this constructor
78 */
79 public PropertyLoaderFilter() {
80 this( DEFAULT_PROPERTIES_FILE_PROPERTY );
81 }
82
83 /**
84 * Constructor that allows the name of the property containg the properties file
85 * to be specified
86 *
87 * @param fileNameProperty property containing the name of file
88 */
89 public PropertyLoaderFilter( final String fileNameProperty ) {
90 this.fileNameProperty = fileNameProperty;
91 }
92
93 /**
94 * Execute the filter action - in this case pre-setting InputField values
95 * with values loaded from a properties file (if present)
96 *
97 * @see org.tp23.antinstaller.runtime.exe.ExecuteFilter
98 * @param ctx context data
99 * @throws InstallException if an error occurred loading pre-defined properties
100 */
101 public void exec(InstallerContext ctx) throws InstallException {
102
103 Installer installer = ctx.getInstaller();
104 String loadDefaults = installer.getLoadDefaults();
105 if(installer.isVerbose()) {
106 ctx.log("loadDefaults attribute:" + loadDefaults);
107 }
108 boolean load = false;
109 if(loadDefaults == null || FALSE.equals(loadDefaults)) {
110 if(installer.isVerbose()) {
111 ctx.log("Not loading defaults");
112 }
113 return;
114 }
115
116 ctx.log( "Checking for predefined properties");
117 Properties predefinedProps = loadPredefinedProperties( ctx, fileNameProperty );
118
119 definedPropertiesCount = predefinedProps.size();
120
121 boolean foundProps = false;
122 if( definedPropertiesCount == 0 ) {
123 ctx.log( "No predefined properties");
124 }
125 else{
126 foundProps = true;
127 }
128
129 if( foundProps && PROMPT.equals(loadDefaults) ) {
130 load = ctx.getMessageRenderer().prompt(res.getString("promptLoadDefaults"));
131 }
132 else if( foundProps && PROMPT_AUTO.equals(loadDefaults)) {
133 load = ctx.getMessageRenderer().prompt(res.getString("promptLoadDefaults"));
134 }
135 else if( foundProps && LOAD.equals(loadDefaults) ) {
136 load = true;
137 }
138
139 if( (!foundProps || !load) &&
140 ctx.isAutoBuild() &&
141 PROMPT.equals(loadDefaults) ) {
142 ctx.log( "Cant run -auto install without properties");
143 throw new AbortException("Install Aborted: cant load ant.install.properties");
144 }
145
146 if(load) {
147 if(installer.isVerbose()) {
148 ctx.log("Loading defaults");
149 }
150
151 // version control
152 String propertiesVersion = predefinedProps.getProperty(PropertiesFileRenderer.INSTALLER_VERSION_PROPERTY);
153 String configVersion = ctx.getInstaller().getVersion();
154 if(propertiesVersion != null) {
155 VersionHelper helper = new VersionHelper();
156 if( ( ! propertiesVersion.equals(configVersion)) &&
157 helper.equalOrHigher(configVersion , propertiesVersion) ) {
158
159 // let major versions pass but prompt for differences
160 if( (! ctx.isAutoBuild()) && helper.majorVersionCompatible(configVersion , propertiesVersion) ){
161 if( ! ctx.getMessageRenderer().prompt(res.getString("propertiesVersionMismatch")) ){
162 throw new AbortException("Install Aborted: existing configuration is not compatible, config version: " + configVersion);
163 }
164 }
165 else {
166 throw new AbortException("Install Aborted: existing configuration is not compatible, config version: " + configVersion);
167 }
168 }
169
170 }
171 else {
172 throw new AbortException("Install Aborted: local ant.install.properties missing config version, must be equal or lower than: " + configVersion);
173 }
174 // end version control
175
176 Page[] allPages = installer.getPages();
177 handleDefaults( ctx, allPages, predefinedProps );
178
179 }
180 }
181
182 /*
183 * Use the supplied properties to pre-populate the page fields
184 */
185 private void handleDefaults( InstallerContext ctx, Page[] allPages, Properties props ) throws InstallException {
186 for( int i = 0; i < allPages.length; i++ ) {
187 OutputField[] fields = allPages[i].getOutputField();
188 setInputValues( ctx, allPages[i], fields, props );
189 }
190 }
191
192 private void setInputValues( InstallerContext ctx, Page page, OutputField[] outputFields, Properties props )
193 throws InstallException {
194 //Should never happen, but guard against it
195 if( outputFields == null ) {
196 return;
197 }
198
199 // find relevant targets
200 String targets = props.getProperty(page.getName() + PropertiesFileRenderer.TARGETS_SUFFIX);
201 List targetsList = splitTargets(targets);
202
203 for (int j = 0; j < outputFields.length; j++) {
204 OutputField field = outputFields[j];
205
206 if( field instanceof ConditionalField ) {
207 ConditionalField condField = (ConditionalField) field;
208 setInputValues( ctx, page, condField.getFields(), props );
209 }
210 else if( field instanceof InputField ) {
211 InputField input = (InputField)field;
212 String propName = input.getProperty();
213 if( props.containsKey( propName ) ) {
214 String value = props.getProperty(propName);
215
216 if( ctx.getInstaller().isDebug() ) {
217 ctx.log( "Setting " + propName + "=" + value );
218 }
219
220 input.setDefaultValue(value); // does not evaluate references
221 input.setInputResult(value);
222 input.setEditted( true );
223
224 if(field instanceof PasswordTextInput) {
225 if(value == null ){
226 ctx.getMessageRenderer().printMessage(res.getString("promptMissingDefaultPassword"));
227
228 }
229 }
230
231 // TARGET TYPES
232 if(field instanceof TargetInput) {
233 // Target and SelectTarget
234 TargetInput tgtInput = (TargetInput)field;
235 page.removeTarget(tgtInput.getIdx());
236 // if target was selected
237 if( ! InputField.isFalse(value)) {
238 page.addTarget(tgtInput.getIdx(), tgtInput.getTarget()); // returns the OS specific suffix if relevant
239 // DEBUG
240 if( ! targetsList.contains(tgtInput.getTarget()) ){
241 // could be caused by someone trying to copy a file across platforms (not a good idea)
242 ctx.log("Defaults error: targets list for page " + page.getName()
243 + " should contain a TargetInput that was true");
244 }
245 }
246 else {
247 if(InputField.isTrue( tgtInput.getForce()) ) {
248 String msg = "Defaults error: forced target for page " + page.getName()
249 + " has been removed";
250 ctx.log(msg);
251 throw new InstallException(msg);
252 }
253 }
254 }
255 if(field instanceof TargetSelectInput) {
256 TargetSelectInput tgtInput = (TargetSelectInput)field;
257 page.removeTarget(tgtInput.getIdx());
258 // one target must be selected (what if the page was not shown??)
259 page.addTarget(tgtInput.getIdx(), value);
260 }
261 }
262 }
263 //TODO: Should properties that are present in properties file but which do not appear
264 // as an InputField be set in the ResultContainer so that they can be used by later
265 // "if" conditions? - no other properties should be loaded separately from additional
266 // resource files if there is a requirement for that using an postDisplayTarget - PH
267 }
268
269 //Page targets should be handled by the config loader process and indexed correctly
270 List pageTargets = page.getTargets(ctx);
271 Iterator iter = targetsList.iterator();
272 while (iter.hasNext()) {
273 String targetPerProps = (String) iter.next();
274 if( ! pageTargets.contains(targetPerProps)) {
275 ctx.log("Defaults warning: targets list for page " + page.getName()
276 + " should contain " + targetPerProps);
277 }
278 }
279
280 }
281
282 /**
283 * Check if external properties have been loaded
284 *
285 * @return <code>true</code> if an external properties file was configured and contained
286 * at least one property
287 */
288 protected boolean isPropertiesLoaded() {
289 return (definedPropertiesCount > 0);
290 }
291
292 /*
293 * Primarily for unit testing
294 */
295 int getPropertiesFoundCount() {
296 return definedPropertiesCount;
297 }
298
299 /**
300 * Load properties from a properties file if present.
301 * The name of the properties file is checked for in the following order.
302 * <p>
303 * If the parameter fileNamePropertyName is not null:
304 * <ul>
305 * <li>the environment is checked for an environmentvariable with that name</li>
306 * <li>java system properties are checked for a property with that name</li>
307 * </ul>
308 * If the file name has not been found, or if <code>fileNamePropertyName == null</code>
309 * then the default file name is used - @see{org.tp23.antinstaller.PropertiesFileRenderer#PROPERTIES_FILE_NAME}
310 *
311 * @param context installer context
312 * @param fileNamePropertyName name of environment variable or java system property containing the
313 * name of the properties file to be loaded or <code>null</code>
314 * @return properties
315 * @throws InstallException if the properties file is missing or an error occurs loading it
316 */
317 private Properties loadPredefinedProperties( final InstallerContext context,
318 final String fileNamePropertyName )
319 throws InstallException {
320
321 Properties contextProps = InstallerContext.getEnvironment();
322 String propertiesFileName = null;
323 boolean failSilently = true;
324
325 if( fileNamePropertyName != null ) {
326 propertiesFileName = contextProps.getProperty( InstallerContext.ENV_PREFIX + fileNamePropertyName );
327
328 if( propertiesFileName == null ) {
329 propertiesFileName =
330 contextProps.getProperty( InstallerContext.JAVA_PREFIX + fileNamePropertyName );
331 }
332
333 if( propertiesFileName != null ) {
334 //Properties have been passed explicitly to installer so must load them
335 failSilently = false;
336 }
337 }
338
339 if( propertiesFileName == null ) {
340 propertiesFileName = PropertiesFileRenderer.PROPERTIES_FILE_NAME;
341 }
342
343 Properties definedProperties = new Properties( );
344
345 if( propertiesFileName != null ) {
346 File definedPropertiesFile = new File( propertiesFileName );
347 context.log( "Loading pre-defined properties from file "
348 + definedPropertiesFile.getAbsolutePath());
349
350 //TODO: Support loading properties file from via classloader as a resource
351 try {
352 FileInputStream istream = new FileInputStream( definedPropertiesFile );
353 definedProperties.load( istream );
354 istream.close();
355 }
356 catch( FileNotFoundException fnfExc ) {
357 if( !failSilently ) {
358 throw new InstallException( "Defined properties file "
359 + definedPropertiesFile.getAbsolutePath()
360 + " doesn't exist" );
361 }
362 }
363 catch( IOException ioExc ) {
364 if( !failSilently ) {
365 throw new InstallException( "Unable to read contents of defined properties file "
366 + definedPropertiesFile.getAbsolutePath(),
367 ioExc );
368 }
369 }
370
371 if( context.getInstaller().isDebug() ) {
372 logPropertiesLoaded( context, definedProperties, definedPropertiesFile );
373 }
374
375 }
376
377 return definedProperties;
378 }
379
380
381 // Debug - log properties loaded
382 private void logPropertiesLoaded( final InstallerContext context,
383 final Properties properties,
384 final File propertiesFile ) {
385 Iterator iterator = properties.keySet().iterator();
386 context.log( "Predefined properties ("
387 + definedPropertiesCount
388 + ") loaded from "
389 + propertiesFile.getAbsolutePath()
390 + "..." );
391 while( iterator.hasNext() ) {
392 String key = (String) iterator.next();
393 context.log( key + "=" + properties.getProperty( key ) );
394 }
395 }
396
397 /*
398 * Could do a String.split(",") but want to avoid 1.4 specific stuff generally
399 * @param commaSeparated
400 * @return
401 */
402 private List splitTargets(String commaSeparated) {
403 if(commaSeparated == null) {
404 return Collections.EMPTY_LIST;
405 }
406 StringTokenizer st = new StringTokenizer(commaSeparated, ",");
407 List targets = new ArrayList();
408 while (st.hasMoreElements()) {
409 String element = st.nextToken();
410 if(element != null){
411 element = element.trim();
412 if(element.length() > 0){
413 targets.add(element.trim());
414 }
415 }
416 }
417 return targets;
418 }
419}
Note: See TracBrowser for help on using the repository browser.