View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.util.database;
5   
6   import java.io.File;
7   import java.io.FileInputStream;
8   import java.io.FileNotFoundException;
9   import java.io.IOException;
10  import java.util.Properties;
11  import java.util.PropertyResourceBundle;
12  import java.util.ResourceBundle;
13  import java.util.logging.Level;
14  import java.util.logging.Logger;
15  
16  /** Encapsulate the settings needed to access database source code.
17   * 
18   * 
19   * @author sturton
20   */
21  public class DBType 
22  {
23    private final static String CLASS_NAME = DBType.class.getCanonicalName();
24  
25    private final static Logger LOGGER = Logger.getLogger(DBType.class.getPackage().getName()); 
26  
27    private final static String INTERNAL_SETTINGS = "[Internal Settings]"; 
28  
29    /**
30     * The names of the properties 
31     */
32    public enum property {
33       USER("user", "Name of the connecting database user"),
34       PASSWORD("password", "The connecting database user's password"),
35       DRIVER("driver", "JDBC driver classname"),
36       CHARACTERSET("characterset","Reader character set"),
37       LANGUAGES("languages", "Comma-separated list of PMD-supported languages"),
38       SCHEMAS("schemas","SchemaSpy compatible regular expression for schemas to be processed"), 
39       SOURCE_TYPES("sourcecodetypes","Comma-separated list of supported source types"),
40       SOURCE_NAMES("sourcecodenames", "Default comma-separated list of source code names to validate"),
41       GET_SOURCE_CODE_STATEMENT("getSourceCodeStatement","SQL92 or Oracle embedded SQL statement to retrieve  code source from the database catalogue"),
42       RETURN_TYPE("returnType", "int equivalent of java.sql.Types return type of getSourceCodeStatement");
43  
44       private String name;
45  
46       private property(String name, String description)
47       {
48        this.name = name; 
49       }
50    } 
51  
52  
53    /**
54     * Where the properties were taken from
55     */
56    private String propertiesSource;
57  
58    /**
59     * Parameters from Properties
60     */
61    private  Properties properties ;
62  
63    //Driver Class 
64    private String driverClass ;
65  
66    //Database CharacterSet
67    private String characterSet;
68  
69    //String to get objects 
70    private String sourceCodeTypes;
71  
72    //Languages to process 
73    private String languages;
74  
75    //Return class for source code 
76    private int sourceCodeReturnType; 
77    
78    /**
79     * 
80     * @param dbType 
81     */
82    public DBType(String dbType) throws Exception 
83    {
84     properties = loadDBProperties(dbType);
85    }
86  
87    /**
88     * Load the most specific dbType for the protocol
89     * @param subProtocol 
90     * @param subnamePrefix 
91   * @throws IOException 
92     */
93    public DBType(String subProtocol, String subnamePrefix) throws IOException
94    {
95  
96       LOGGER.fine("subProtocol="+subProtocol+", subnamePrefix="+subnamePrefix);
97       
98       if (null == subProtocol &&  null == subnamePrefix)
99       {
100        throw new RuntimeException("subProtocol and subnamePrefix cannot both be null");
101      }
102      else
103      {
104 
105        properties = null;
106 
107        //Attempt subnamePrefix before subProtocol
108        if (null != subnamePrefix 
109           && null != (properties = loadDBProperties(subnamePrefix)) 
110           )
111        {
112           LOGGER.log(Level.FINE, "DBType found using subnamePrefix={0}", subnamePrefix); 
113        }
114        else if (null != (properties = loadDBProperties(subProtocol) ) )
115        {
116           LOGGER.log(Level.FINE, "DBType found using subProtocol={0}", subProtocol); 
117        }
118        else
119        { 
120          throw new RuntimeException(String.format("Could not locate DBType properties using subProtocol=%s and subnamePrefix=%s"
121                              ,subProtocol
122                              ,subnamePrefix
123                              )
124                              );
125        }
126               
127      }
128   }
129 
130   public Properties getProperties () {
131 
132     return properties;  
133   }
134 
135      /**
136      * Load properties from one or more files or resources.
137      * 
138      *<p>This method recursively finds property files or JAR resources matching {@matchstring}. </p>.
139      *<p>The method is intended to be able to use , so any   
140      *
141      * @param matchString
142      * @return "current" set of properties (from one or more resources/property files)
143      */
144     private Properties loadDBProperties(String matchString) throws IOException {
145         LOGGER.entering(CLASS_NAME, matchString);
146         //Locale locale = Control.g;
147         ResourceBundle resourceBundle = null;
148 
149         LOGGER.finest("class_path+"+System.getProperty("java.class.path"));
150 
151         /*
152          * Attempt to match properties files in this order:-
153          * File path with properties suffix
154          * File path without properties suffix
155          * Resource without class prefix  
156          * Resource with class prefix  
157          * 
158          */
159         try {
160             File propertiesFile = new File(matchString);
161             LOGGER.finest("Attempting File no file suffix: " + matchString);
162             resourceBundle = new PropertyResourceBundle(new FileInputStream(propertiesFile));
163             propertiesSource = propertiesFile.getAbsolutePath();
164             LOGGER.finest("FileSystemWithoutExtension");
165         } catch (FileNotFoundException notFoundOnFilesystemWithoutExtension) {
166             LOGGER.finest("notFoundOnFilesystemWithoutExtension");
167             LOGGER.finest("Attempting File with added file suffix: " 
168                                 + matchString + ".properties");
169             try {
170                 File propertiesFile = new File(matchString + ".properties");
171                 resourceBundle = new PropertyResourceBundle(new FileInputStream(propertiesFile));
172                 propertiesSource = propertiesFile.getAbsolutePath();
173                 LOGGER.finest("FileSystemWithExtension");
174             } catch (FileNotFoundException notFoundOnFilesystemWithExtensionTackedOn) {
175                 LOGGER.finest("Attempting JARWithoutClassPrefix: " + matchString);
176                 try {
177                     resourceBundle = ResourceBundle.getBundle(matchString);
178                     propertiesSource = "[" + INTERNAL_SETTINGS + "]" + File.separator 
179                                         + matchString + ".properties";
180                     LOGGER.finest("InJarWithoutPath");
181                 } catch (Exception notInJarWithoutPath) {
182                   LOGGER.finest("Attempting JARWithClass prefix: " + DBType.CLASS_NAME + "." + matchString);
183                   try {
184                       resourceBundle = ResourceBundle.getBundle(DBType.CLASS_NAME + "." + matchString);
185                       propertiesSource = "[" + INTERNAL_SETTINGS + "]" + File.separator 
186                                           + matchString + ".properties";
187                       LOGGER.finest("found InJarWithPath");
188                   } catch (Exception notInJarWithPath) {
189                       notInJarWithPath.printStackTrace();
190                       notFoundOnFilesystemWithExtensionTackedOn.printStackTrace();
191                       throw new RuntimeException (" Could not locate DBTYpe settings : "+matchString,notInJarWithPath);
192                   }
193                 }
194             }
195         }
196 
197         //Properties in this matched resource
198         Properties matchedProperties = getResourceBundleAsProperties(resourceBundle);
199         resourceBundle = null;
200         String saveLoadedFrom = getPropertiesSource(); 
201 
202 
203         /*
204          * If the matched properties contain the "extends" key,
205          * use the value as a matchstring, to recursively set the properties 
206          * before overwriting any previous properties with the matched properties.
207          */
208         String extendedPropertyFile = (String)matchedProperties.remove("extends"); 
209         if (null != extendedPropertyFile && !"".equals(extendedPropertyFile.trim()) ) {
210             Properties extendedProperties = loadDBProperties(extendedPropertyFile.trim());
211 
212             // Overwrite extended properties with properties in the matched resource
213             extendedProperties.putAll(matchedProperties);
214             matchedProperties = extendedProperties;
215         }
216 
217         /*
218          * Record the location of the original matched resource/property file, and the current
219          * set of properties secured. 
220          */
221         propertiesSource = saveLoadedFrom;
222         setProperties(matchedProperties);
223 
224         return matchedProperties; 
225     }
226 
227     /**
228      * Options that are specific to a type of database.  E.g. things like <code>host</code>,
229      * <code>port</code> or <code>db</code>, but <b>don't</b> have a setter in this class.
230      *
231      * @param dbSpecificOptions
232      */
233 
234     /**
235      * Convert <code>resourceBundle</code> to usable {@Properties}. 
236      *
237      * @param resourceBundle ResourceBundle
238      * @return Properties
239      */
240     public static Properties getResourceBundleAsProperties(ResourceBundle resourceBundle) {
241         Properties properties = new Properties();
242         for (String key : resourceBundle.keySet() )
243         {
244             properties.put(key, resourceBundle.getObject(key));
245         }
246 
247         return properties;
248     }
249 
250     public boolean equals(DBType other)
251     {
252      
253       return 
254       this.getPropertiesSource().equals(other.getPropertiesSource()) &&
255       this.getProperties().equals(other.getProperties())  &&
256       this.getDriverClass().equals(other.getDriverClass())  &&
257       this.getCharacterSet().equals(other.getCharacterSet()) &&
258       this.getSourceCodeTypes().equals(other.getSourceCodeTypes()) &&
259       this.getLanguages().equals(other.getLanguages()) &&
260       this.getSourceCodeReturnType() == other.getSourceCodeReturnType()
261       ; 
262     }
263 
264   /**
265    * @return the driverClass
266    */
267   public String getDriverClass() {
268     return driverClass;
269   }
270 
271   /**
272    * @return the characterSet
273    */
274   public String getCharacterSet() {
275     return characterSet;
276   }
277 
278   /**
279    * @return the sourceCodeTypes
280    */
281   public String getSourceCodeTypes() {
282     return sourceCodeTypes;
283   }
284 
285   /**
286    * @return the languages
287    */
288   public String getLanguages() {
289     return languages;
290   }
291 
292   /**
293    * @return the sourceCodeReturnType
294    */
295   public int getSourceCodeReturnType() {
296     return sourceCodeReturnType;
297   }
298 
299   /**
300    * @return the propertiesSource
301    */
302   public String getPropertiesSource() {
303     return propertiesSource;
304   }
305 
306   /**
307    * @param properties the properties to set
308    */
309   public void setProperties(Properties properties) {
310     this.properties = properties;
311 
312     //Driver Class 
313     if (null != this.properties.getProperty("driver"))
314     {
315       this.driverClass = this.properties.getProperty("driver");
316     }
317 
318     //Database CharacterSet
319     if (null != this.properties.getProperty("characterset"))
320     {
321       this.characterSet = this.properties.getProperty("characterset");
322     }
323 
324     //String to get objects 
325     if (null != this.properties.getProperty("sourcecodetypes"))
326     {
327       this.sourceCodeTypes = this.properties.getProperty("sourcecodetypes");
328     }
329 
330     //Languages to process 
331     if (null != this.properties.getProperty("languages"))
332     {
333       this.languages = this.properties.getProperty("languages");
334     }
335 
336     //Return class for source code 
337     if (null != this.properties.getProperty("returnType"))
338     {
339       LOGGER.finest("returnType" + this.properties.getProperty("returnType")  );
340       this.sourceCodeReturnType  = Integer.parseInt(this.properties.getProperty("returnType"));
341     }
342 
343   }
344 
345  public String toString()
346  {
347    return CLASS_NAME+"@"+propertiesSource;
348  }
349 }