001/* 002 * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the The BSD 3-Clause License 006 * which accompanies this distribution, and is available at 007 * http://opensource.org/licenses/BSD-3-Clause 008 * 009 * Contributors: 010 * Tada AB 011 * Purdue University 012 * Chapman Flack 013 */ 014package org.postgresql.pljava.example.annotation; 015 016import org.postgresql.pljava.annotation.Function; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.util.Collections; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.Properties; 026import java.util.ResourceBundle; 027import java.util.logging.Logger; 028 029import org.postgresql.pljava.ResultSetProvider; 030import org.postgresql.pljava.annotation.SQLAction; 031 032/** 033 * An example that retrieves a {@code Properties} resource, and returns 034 * (key,value) rows from it by implementing the {@code ResultSetProvider} 035 * interface. 036 * @author Thomas Hallgren 037 */ 038@SQLAction(requires = {"propertyExampleAnno", "propertyExampleRB"}, install = { 039 "WITH" + 040 " expected AS (VALUES" + 041 " ('adjective' ::varchar(200), 'avaricious' ::varchar(200))," + 042 " ('noun', 'platypus')" + 043 " )" + 044 "SELECT" + 045 " CASE WHEN" + 046 " 2 = count(prop) AND every(prop IN (SELECT expected FROM expected))" + 047 " THEN javatest.logmessage('INFO', 'get resource passes')" + 048 " ELSE javatest.logmessage('WARNING', 'get resource fails')" + 049 " END" + 050 " FROM" + 051 " propertyExampleAnno() AS prop", 052 053 "WITH" + 054 " expected AS (VALUES" + 055 " ('adjective' ::varchar(200), 'avaricious' ::varchar(200))," + 056 " ('noun', 'platypus')" + 057 " )" + 058 "SELECT" + 059 " CASE WHEN" + 060 " 2 = count(prop) AND every(prop IN (SELECT expected FROM expected))" + 061 " THEN javatest.logmessage('INFO', 'get ResourceBundle passes')" + 062 " ELSE javatest.logmessage('WARNING', 'get ResourceBundle fails')" + 063 " END" + 064 " FROM" + 065 " propertyExampleRB() AS prop" 066}) 067public class UsingProperties implements ResultSetProvider.Large 068{ 069 private static Logger s_logger = Logger.getAnonymousLogger(); 070 private final Iterator m_propertyIterator; 071 072 public UsingProperties() 073 throws IOException 074 { 075 Properties v = new Properties(); 076 InputStream propStream = 077 this.getClass().getResourceAsStream("example.properties"); 078 079 if(propStream == null) 080 { 081 s_logger.fine("example.properties was null"); 082 m_propertyIterator = Collections.EMPTY_SET.iterator(); 083 } 084 else 085 { 086 v.load(propStream); 087 propStream.close(); 088 s_logger.fine("example.properties has " + v.size() + " entries"); 089 m_propertyIterator = v.entrySet().iterator(); 090 } 091 } 092 093 /** 094 * This constructor (distinguished by signature) reads the same property 095 * file, but using the {@code ResourceBundle} machinery instead of 096 * {@code Properties}. 097 */ 098 private UsingProperties(Void usingResourceBundle) 099 { 100 ResourceBundle b = 101 ResourceBundle.getBundle(getClass().getPackageName() + ".example"); 102 103 Iterator<String> keys = b.getKeys().asIterator(); 104 105 m_propertyIterator = new Iterator<Map.Entry<String,String>>() 106 { 107 public boolean hasNext() 108 { 109 return keys.hasNext(); 110 } 111 112 public Map.Entry<String,String> next() 113 { 114 String k = keys.next(); 115 return Map.entry(k, b.getString(k)); 116 } 117 }; 118 } 119 120 public boolean assignRowValues(ResultSet receiver, long currentRow) 121 throws SQLException 122 { 123 if(!m_propertyIterator.hasNext()) 124 { 125 s_logger.fine("no more rows, returning false"); 126 return false; 127 } 128 Map.Entry propEntry = (Map.Entry)m_propertyIterator.next(); 129 receiver.updateString(1, (String)propEntry.getKey()); 130 receiver.updateString(2, (String)propEntry.getValue()); 131 // s_logger.fine("next row created, returning true"); 132 return true; 133 } 134 135 /** 136 * Return the contents of the {@code example.properties} resource, 137 * one (key,value) row per entry. 138 */ 139 @Function(type = "javatest._properties", provides = "propertyExampleAnno") 140 public static ResultSetProvider propertyExampleAnno() 141 throws SQLException 142 { 143 try 144 { 145 return new UsingProperties(); 146 } 147 catch(IOException e) 148 { 149 throw new SQLException("Error reading properties", e.getMessage()); 150 } 151 } 152 153 /** 154 * Return the contents of the {@code example.properties} resource, 155 * one (key,value) row per entry, using {@code ResourceBundle} to load it. 156 */ 157 @Function(type = "javatest._properties", provides = "propertyExampleRB") 158 public static ResultSetProvider propertyExampleRB() 159 throws SQLException 160 { 161 return new UsingProperties(null); 162 } 163 164 public void close() 165 { 166 } 167}