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 018 package org.apache.commons.exec.environment; 019 020 import java.io.BufferedReader; 021 import java.io.ByteArrayOutputStream; 022 import java.io.File; 023 import java.io.IOException; 024 import java.io.StringReader; 025 import java.lang.reflect.InvocationTargetException; 026 import java.lang.reflect.Method; 027 import java.util.Comparator; 028 import java.util.HashMap; 029 import java.util.Map; 030 import java.util.TreeMap; 031 032 import org.apache.commons.exec.CommandLine; 033 import org.apache.commons.exec.DefaultExecutor; 034 import org.apache.commons.exec.Executor; 035 import org.apache.commons.exec.OS; 036 import org.apache.commons.exec.PumpStreamHandler; 037 038 /** 039 * Helper class to determine the environment variable 040 * for the OS. Depending on the JDK the environment 041 * variables can be either retrieved directly from the 042 * JVM or requires starting a process to get them running 043 * an OS command line. 044 */ 045 public class DefaultProcessingEnvironment { 046 047 /** the line separator of the system */ 048 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 049 050 /** the environment variables of the process */ 051 protected Map procEnvironment; 052 053 /** 054 * Find the list of environment variables for this process. 055 * 056 * @return a map containing the environment variables 057 * @throws IOException obtaining the environment variables failed 058 */ 059 public synchronized Map getProcEnvironment() throws IOException { 060 061 if(procEnvironment == null) { 062 procEnvironment = this.createProcEnvironment(); 063 } 064 065 // create a copy of the map just in case that 066 // anyone is going to modifiy it, e.g. removing 067 // or setting an evironment variable 068 Map copy = createEnvironmentMap(); 069 copy.putAll(procEnvironment); 070 return copy; 071 } 072 073 /** 074 * Find the list of environment variables for this process. 075 * 076 * @return a amp containing the environment variables 077 * @throws IOException the operation failed 078 */ 079 protected Map createProcEnvironment() throws IOException { 080 if (procEnvironment == null) { 081 try { 082 Method getenvs = System.class.getMethod( "getenv", (java.lang.Class[]) null ); 083 Map env = (Map) getenvs.invoke( null, (java.lang.Object[]) null ); 084 procEnvironment = createEnvironmentMap(); 085 procEnvironment.putAll(env); 086 } catch ( NoSuchMethodException e ) { 087 // ok, just not on JDK 1.5 088 } catch ( IllegalAccessException e ) { 089 // Unexpected error obtaining environment - using JDK 1.4 method 090 } catch ( InvocationTargetException e ) { 091 // Unexpected error obtaining environment - using JDK 1.4 method 092 } 093 } 094 095 if(procEnvironment == null) { 096 procEnvironment = createEnvironmentMap(); 097 BufferedReader in = runProcEnvCommand(); 098 099 String var = null; 100 String line; 101 while ((line = in.readLine()) != null) { 102 if (line.indexOf('=') == -1) { 103 // Chunk part of previous env var (UNIX env vars can 104 // contain embedded new lines). 105 if (var == null) { 106 var = LINE_SEPARATOR + line; 107 } else { 108 var += LINE_SEPARATOR + line; 109 } 110 } else { 111 // New env var...append the previous one if we have it. 112 if (var != null) { 113 EnvironmentUtils.addVariableToEnvironment(procEnvironment, var); 114 } 115 var = line; 116 } 117 } 118 // Since we "look ahead" before adding, there's one last env var. 119 if (var != null) { 120 EnvironmentUtils.addVariableToEnvironment(procEnvironment, var); 121 } 122 } 123 return procEnvironment; 124 } 125 126 /** 127 * Start a process to list the environment variables. 128 * 129 * @return a reader containing the output of the process 130 * @throws IOException starting the process failed 131 */ 132 protected BufferedReader runProcEnvCommand() throws IOException { 133 ByteArrayOutputStream out = new ByteArrayOutputStream(); 134 Executor exe = new DefaultExecutor(); 135 exe.setStreamHandler(new PumpStreamHandler(out)); 136 // ignore the exit value - Just try to use what we got 137 exe.execute(getProcEnvCommand()); 138 return new BufferedReader(new StringReader(toString(out))); 139 } 140 141 /** 142 * Determine the OS specific command line to get a list of environment 143 * variables. 144 * 145 * @return the command line 146 */ 147 protected CommandLine getProcEnvCommand() { 148 String executable; 149 String[] arguments = null; 150 if (OS.isFamilyOS2()) { 151 // OS/2 - use same mechanism as Windows 2000 152 executable = "cmd"; 153 154 arguments = new String[] {"/c", "set"}; 155 } else if (OS.isFamilyWindows()) { 156 // Determine if we're running under XP/2000/NT or 98/95 157 if (OS.isFamilyWin9x()) { 158 executable = "command.com"; 159 // Windows 98/95 160 } else { 161 executable = "cmd"; 162 // Windows XP/2000/NT/2003 163 } 164 arguments = new String[] {"/c", "set"}; 165 } else if (OS.isFamilyZOS() || OS.isFamilyUnix()) { 166 // On most systems one could use: /bin/sh -c env 167 168 // Some systems have /bin/env, others /usr/bin/env, just try 169 if (new File("/bin/env").canRead()) { 170 executable = "/bin/env"; 171 } else if (new File("/usr/bin/env").canRead()) { 172 executable = "/usr/bin/env"; 173 } else { 174 // rely on PATH 175 executable = "env"; 176 } 177 } else if (OS.isFamilyNetware() || OS.isFamilyOS400()) { 178 // rely on PATH 179 executable = "env"; 180 } else { 181 // MAC OS 9 and previous 182 // TODO: I have no idea how to get it, someone must fix it 183 executable = null; 184 } 185 CommandLine commandLine = null; 186 if(executable != null) { 187 commandLine = new CommandLine(executable); 188 commandLine.addArguments(arguments); 189 } 190 return commandLine; 191 } 192 193 /** 194 * ByteArrayOutputStream#toString doesn't seem to work reliably on OS/390, 195 * at least not the way we use it in the execution context. 196 * 197 * @param bos 198 * the output stream that one wants to read 199 * @return the output stream as a string, read with special encodings in the 200 * case of z/os and os/400 201 */ 202 private String toString(final ByteArrayOutputStream bos) { 203 if (OS.isFamilyZOS()) { 204 try { 205 return bos.toString("Cp1047"); 206 } catch (java.io.UnsupportedEncodingException e) { 207 // noop default encoding used 208 } 209 } else if (OS.isFamilyOS400()) { 210 try { 211 return bos.toString("Cp500"); 212 } catch (java.io.UnsupportedEncodingException e) { 213 // noop default encoding used 214 } 215 } 216 return bos.toString(); 217 } 218 219 /** 220 * Creates a map that obeys the casing rules of the current platform for key 221 * lookup. E.g. on a Windows platform, the map keys will be 222 * case-insensitive. 223 * 224 * @return The map for storage of environment variables, never 225 * <code>null</code>. 226 */ 227 private Map createEnvironmentMap() { 228 if (OS.isFamilyWindows()) { 229 return new TreeMap(new Comparator() { 230 public int compare(Object arg0, Object arg1) { 231 String key0 = (String) arg0; 232 String key1 = (String) arg1; 233 return key0.compareToIgnoreCase(key1); 234 } 235 }); 236 } else { 237 return new HashMap(); 238 } 239 } 240 241 }