001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.configuration;
018    
019    import java.lang.reflect.Method;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.Map;
024    
025    import org.apache.commons.lang.SystemUtils;
026    import org.apache.tools.ant.taskdefs.Execute;
027    
028    /**
029     * <p>
030     * A Configuration implementation that reads the platform specific environment
031     * variables. On pre java5 JRE it uses Ant Execute task to read the environment.
032     * (in this case ant must be present in classpath). On java >= 5 JRE it uses
033     * <code>java.lang.System#getenv()</code> and ant is not required.
034     * </p>
035     * <p>
036     * This configuration implementation is read-only. It allows read access to the
037     * defined OS environment variables, but their values cannot be changed.
038     * </p>
039     * <p>
040     * Usage of this class is easy: After an instance has been created the get
041     * methods provided by the <code>Configuration</code> interface can be used
042     * for querying environment variables, e.g.:
043     *
044     * <pre>
045     * Configuration envConfig = new EnvironmentConfiguration();
046     * System.out.println("JAVA_HOME=" + envConfig.getString("JAVA_HOME");
047     * </pre>
048     *
049     * </p>
050     *
051     * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
052     * @see org.apache.tools.ant.taskdefs.Execute#getProcEnvironment()
053     * @since 1.5
054     */
055    public class EnvironmentConfiguration extends AbstractConfiguration
056    {
057        /** Constant for the name of the getenv() method. */
058        private static final String METHOD_NAME = "getenv";
059    
060        /** Constant for the Java version 1.5. */
061        private static final int VERSION_1_5 = 150;
062    
063        /** Stores the environment properties. */
064        private Map environment;
065    
066        /**
067         * Constructor.
068         */
069        public EnvironmentConfiguration()
070        {
071            if (SystemUtils.isJavaVersionAtLeast(VERSION_1_5))
072            {
073                extractProperties15();
074            }
075            else
076            {
077                extractProperties14();
078            }
079        }
080    
081        /**
082         * Adds a property to this configuration. Because this configuration is
083         * read-only, this operation is not allowed and will cause an exception.
084         *
085         * @param key the key of the property to be added
086         * @param value the property value
087         */
088        protected void addPropertyDirect(String key, Object value)
089        {
090            throw new UnsupportedOperationException("Configuration is read-only!");
091        }
092    
093        /**
094         * {@inheritDoc}
095         *
096         * @see org.apache.commons.configuration.AbstractConfiguration#containsKey(java.lang.String)
097         */
098        public boolean containsKey(String key)
099        {
100            return environment.containsKey(key);
101        }
102    
103        /**
104         * {@inheritDoc}
105         *
106         * @see org.apache.commons.configuration.AbstractConfiguration#getKeys()
107         */
108        public Iterator getKeys()
109        {
110            return environment.keySet().iterator();
111        }
112    
113        /**
114         * {@inheritDoc}
115         *
116         * @see org.apache.commons.configuration.AbstractConfiguration#getProperty(java.lang.String)
117         */
118        public Object getProperty(String key)
119        {
120            return environment.get(key);
121        }
122    
123        /**
124         * {@inheritDoc}
125         *
126         * @see org.apache.commons.configuration.AbstractConfiguration#isEmpty()
127         */
128        public boolean isEmpty()
129        {
130            return environment.isEmpty();
131        }
132    
133        /**
134         * Removes a property from this configuration. Because this configuration is
135         * read-only, this operation is not allowed and will cause an exception.
136         *
137         * @param key the key of the property to be removed
138         */
139        public void clearProperty(String key)
140        {
141            throw new UnsupportedOperationException("Configuration is read-only!");
142        }
143    
144        /**
145         * Removes all properties from this configuration. Because this
146         * configuration is read-only, this operation is not allowed and will cause
147         * an exception.
148         */
149        public void clear()
150        {
151            throw new UnsupportedOperationException("Configuration is read-only!");
152        }
153    
154        /**
155         * Extracts environment properties on a JRE &lt; 1.5. This implementation
156         * uses ant for this purpose.
157         */
158        void extractProperties14()
159        {
160            extractPropertiesFromCollection(Execute.getProcEnvironment());
161        }
162    
163        /**
164         * An internally used method for processing a collection with environment
165         * entries. The collection must contain strings like
166         * <code>property=value</code>. Such a collection is returned by ant.
167         *
168         * @param env the collection with the properties
169         */
170        void extractPropertiesFromCollection(Collection env)
171        {
172            environment = new HashMap();
173            for (Iterator it = env.iterator(); it.hasNext();)
174            {
175                String entry = (String) it.next();
176                int pos = entry.indexOf('=');
177                if (pos == -1)
178                {
179                    getLogger().warn("Ignoring: " + entry);
180                }
181                else
182                {
183                    environment.put(entry.substring(0, pos), entry
184                            .substring(pos + 1));
185                }
186            }
187        }
188    
189        /**
190         * Extracts environment properties on a JR &gt;= 1.5. From this Java version
191         * on, there is an official way of doing this. However because the code
192         * should compile on lower Java versions, too, we have to invoke the method
193         * using reflection.
194         */
195        void extractProperties15()
196        {
197            try
198            {
199                Method method = System.class.getMethod(METHOD_NAME, null);
200                environment = (Map) method.invoke(null, null);
201            }
202            catch (Exception ex)
203            {
204                // this should normally not happen on a JRE >= 1.5
205                throw new ConfigurationRuntimeException(
206                        "Error when accessing environment properties", ex);
207            }
208        }
209    }