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 */ 017package org.apache.camel.component.properties; 018 019import java.io.BufferedReader; 020import java.io.FileInputStream; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.InputStreamReader; 025import java.io.Reader; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.util.IOHelper; 032 033/** 034 * Default {@link org.apache.camel.component.properties.PropertiesResolver} which can resolve properties 035 * from file and classpath. 036 * <p/> 037 * You can denote <tt>classpath:</tt> or <tt>file:</tt> as prefix in the uri to select whether the file 038 * is located in the classpath or on the file system. 039 * 040 * @version 041 */ 042public class DefaultPropertiesResolver implements PropertiesResolver { 043 044 private final PropertiesComponent propertiesComponent; 045 046 public DefaultPropertiesResolver(PropertiesComponent propertiesComponent) { 047 this.propertiesComponent = propertiesComponent; 048 } 049 050 public Properties resolveProperties(CamelContext context, boolean ignoreMissingLocation, List<PropertiesLocation> locations) throws Exception { 051 Properties answer = new Properties(); 052 Properties prop; 053 054 for (PropertiesLocation location : locations) { 055 switch(location.getResolver()) { 056 case "ref": 057 prop = loadPropertiesFromRegistry(context, ignoreMissingLocation, location); 058 prop = prepareLoadedProperties(prop); 059 answer.putAll(prop); 060 break; 061 case "file": 062 prop = loadPropertiesFromFilePath(context, ignoreMissingLocation, location); 063 prop = prepareLoadedProperties(prop); 064 answer.putAll(prop); 065 break; 066 case "classpath": 067 default: 068 // default to classpath 069 prop = loadPropertiesFromClasspath(context, ignoreMissingLocation, location); 070 prop = prepareLoadedProperties(prop); 071 answer.putAll(prop); 072 break; 073 } 074 } 075 076 return answer; 077 } 078 079 protected Properties loadPropertiesFromFilePath(CamelContext context, boolean ignoreMissingLocation, PropertiesLocation location) throws IOException { 080 Properties answer = new Properties(); 081 String path = location.getPath(); 082 083 InputStream is = null; 084 Reader reader = null; 085 try { 086 is = new FileInputStream(path); 087 if (propertiesComponent.getEncoding() != null) { 088 reader = new BufferedReader(new InputStreamReader(is, propertiesComponent.getEncoding())); 089 answer.load(reader); 090 } else { 091 answer.load(is); 092 } 093 } catch (FileNotFoundException e) { 094 if (!ignoreMissingLocation && !location.isOptional()) { 095 throw e; 096 } 097 } finally { 098 IOHelper.close(reader, is); 099 } 100 101 return answer; 102 } 103 104 protected Properties loadPropertiesFromClasspath(CamelContext context, boolean ignoreMissingLocation, PropertiesLocation location) throws IOException { 105 Properties answer = new Properties(); 106 String path = location.getPath(); 107 108 InputStream is = context.getClassResolver().loadResourceAsStream(path); 109 Reader reader = null; 110 if (is == null) { 111 if (!ignoreMissingLocation && !location.isOptional()) { 112 throw new FileNotFoundException("Properties file " + path + " not found in classpath"); 113 } 114 } else { 115 try { 116 if (propertiesComponent.getEncoding() != null) { 117 reader = new BufferedReader(new InputStreamReader(is, propertiesComponent.getEncoding())); 118 answer.load(reader); 119 } else { 120 answer.load(is); 121 } 122 } finally { 123 IOHelper.close(reader, is); 124 } 125 } 126 return answer; 127 } 128 129 @SuppressWarnings({"rawtypes", "unchecked"}) 130 protected Properties loadPropertiesFromRegistry(CamelContext context, boolean ignoreMissingLocation, PropertiesLocation location) throws IOException { 131 String path = location.getPath(); 132 Properties answer; 133 try { 134 answer = context.getRegistry().lookupByNameAndType(path, Properties.class); 135 } catch (Exception ex) { 136 // just look up the Map as a fault back 137 Map map = context.getRegistry().lookupByNameAndType(path, Map.class); 138 answer = new Properties(); 139 answer.putAll(map); 140 } 141 if (answer == null && (!ignoreMissingLocation && !location.isOptional())) { 142 throw new FileNotFoundException("Properties " + path + " not found in registry"); 143 } 144 return answer != null ? answer : new Properties(); 145 } 146 147 /** 148 * Strategy to prepare loaded properties before being used by Camel. 149 * <p/> 150 * This implementation will ensure values are trimmed, as loading properties from 151 * a file with values having trailing spaces is not automatic trimmed by the Properties API 152 * from the JDK. 153 * 154 * @param properties the properties 155 * @return the prepared properties 156 */ 157 protected Properties prepareLoadedProperties(Properties properties) { 158 Properties answer = new Properties(); 159 for (Map.Entry<Object, Object> entry : properties.entrySet()) { 160 Object key = entry.getKey(); 161 Object value = entry.getValue(); 162 if (value instanceof String) { 163 String s = (String) value; 164 165 // trim any trailing spaces which can be a problem when loading from 166 // a properties file, note that java.util.Properties does already this 167 // for any potential leading spaces so there's nothing to do there 168 value = trimTrailingWhitespaces(s); 169 } 170 answer.put(key, value); 171 } 172 return answer; 173 } 174 175 private static String trimTrailingWhitespaces(String s) { 176 int endIndex = s.length(); 177 for (int index = s.length() - 1; index >= 0; index--) { 178 if (s.charAt(index) == ' ') { 179 endIndex = index; 180 } else { 181 break; 182 } 183 } 184 String answer = s.substring(0, endIndex); 185 return answer; 186 } 187 188}