source: release-kits/lirk3/bin/ant-installer/src/org/tp23/antinstaller/runtime/exe/PropertyLoaderFilter.java@ 14982

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

initial import of LiRK3

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("org.tp23.antinstaller.renderer.Res");
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.