1414import org .springframework .util .StreamUtils ;
1515
1616import javax .sql .DataSource ;
17+ import java .io .ByteArrayOutputStream ;
1718import java .io .IOException ;
1819import java .io .InputStream ;
20+ import java .nio .charset .StandardCharsets ;
1921import java .sql .Connection ;
2022import java .sql .PreparedStatement ;
2123import java .sql .ResultSet ;
2527import java .util .HashMap ;
2628import java .util .List ;
2729import java .util .Map ;
30+ import java .util .Objects ;
2831
2932import static cz .metacentrum .perun .core .blImpl .GroupsManagerBlImpl .GROUP_DESCRIPTION ;
3033import static cz .metacentrum .perun .core .blImpl .GroupsManagerBlImpl .GROUP_NAME ;
@@ -239,33 +242,29 @@ public List<Map<String, String>> getSubjectGroups(Map<String, String> groupAttri
239242 * @param query to select subject groups
240243 * @return list of subjects
241244 */
242- protected List <Map <String ,String >> groupQuery (String query ) throws InternalErrorException {
245+ protected List <Map <String ,String >> groupQuery (String query ) {
243246 try (Connection con = getDataSource ().getConnection ()) {
244247 try (PreparedStatement st = con .prepareStatement (query )) {
245248 try (ResultSet rs = st .executeQuery ()) {
246249 List <Map <String , String >> subjects = new ArrayList <>();
247250 log .trace ("Query {}" , query );
248- ResultSetMetaData metaData = rs .getMetaData ();
249- List <ColumnMapping > columnMappings = new ArrayList <>(metaData .getColumnCount ());
250- for (int i = 1 ; i <= metaData .getColumnCount (); i ++) {
251- String columnName = metaData .getColumnLabel (i );
252- String baseName = matchingGroupColumnName (columnName );
253- if (baseName != null ) {
254- columnMappings .add (new ColumnMapping (i , baseName , false ));
255- }
256- }
257251 while (rs .next ()) {
258252 Map <String , String > map = new HashMap <>();
259- for (ColumnMapping columnMapping : columnMappings ) {
260- map .put (columnMapping .attributeName , rs .getString (columnMapping .columnIndex ));
261- }
253+ GROUP_SYNC_DEFAULT_DATA .forEach (
254+ column -> map .put (column , readGroupSyncRequiredData (rs , column ))
255+ );
256+ map .putAll (parseAdditionalAttributeData (rs ));
262257 subjects .add (map );
263258 }
259+
264260 log .debug ("Returning {} subjects from external source {}" , subjects .size (), this );
265261 return subjects ;
262+ } catch (SQLException e ) {
263+ log .error ("SQL exception during searching for subject '{}'" , query );
264+ throw new InternalErrorException (e );
266265 }
267266 } catch (SQLException e ) {
268- log .error ("SQL exception during searching for subject '{}'" , query );
267+ log .error ("SQL exception during the preparation of query statement '{}'" , query );
269268 throw new InternalErrorException (e );
270269 }
271270 } catch (SQLException e ) {
@@ -274,6 +273,53 @@ protected List<Map<String,String>> groupQuery(String query) throws InternalError
274273 }
275274 }
276275
276+ /**
277+ * Decodes a full attribute name from the given value. For used mappings,
278+ * see attributeNameMapping.
279+ *
280+ * @param value which will be mapped
281+ * @return attribute full name
282+ */
283+ private String mapAttributeNames (String value ) {
284+ String [] attributeRaw = value .split (":" , 3 );
285+ String attributeName = null ;
286+ if (!ATTRIBUTE_NAME_MAPPING .containsKey (attributeRaw [0 ])) {
287+ log .warn ("Unknown attribute type '{}', attributeRaw {}" , attributeRaw [0 ], attributeRaw );
288+ } else if (!ATTRIBUTE_NAME_MAPPING .containsKey (attributeRaw [1 ])) {
289+ log .warn ("Unknown attribute type '{}', attributeRaw {}" , attributeRaw [1 ], attributeRaw );
290+ } else {
291+ attributeName = ATTRIBUTE_NAME_MAPPING .get (attributeRaw [0 ]) + ATTRIBUTE_NAME_MAPPING .get (attributeRaw [1 ]) +
292+ attributeRaw [2 ];
293+ }
294+ return attributeName ;
295+ }
296+
297+
298+ /**
299+ * Parse additional data from the given result set.
300+ *
301+ * @param rs result set with attribute data
302+ * @throws SQLException SQLException
303+ */
304+ private Map <String , String > parseAdditionalAttributeData (ResultSet rs ) throws SQLException {
305+ Map <String , String > additionalAttributes = new HashMap <>();
306+ for (int i = 1 ; i <= rs .getMetaData ().getColumnCount (); i ++) {
307+ String columnName = rs .getMetaData ().getColumnLabel (i );
308+ if (columnName .contains (":" )) {
309+ String attrName = mapAttributeNames (columnName );
310+ String attributeValue ;
311+ if (Objects .equals (rs .getMetaData ().getColumnTypeName (i ), "BLOB" )) {
312+ attributeValue = parseBlobValue (rs .getBinaryStream (i ), columnName );
313+ } else {
314+ // let driver convert the type to string
315+ attributeValue = rs .getString (i );
316+ }
317+ additionalAttributes .put (attrName , attributeValue );
318+ }
319+ }
320+ return additionalAttributes ;
321+ }
322+
277323 protected DataSource getDataSource () {
278324 if (dataSource == null ) {
279325 Map <String , String > attributes = this .getAttributes ();
@@ -287,4 +333,47 @@ protected DataSource getDataSource() {
287333 return dataSource ;
288334 }
289335
336+ /**
337+ * Parse blob value from given input stream.
338+ *
339+ * @param inputStream input stream with a blob value
340+ * @param columnName name of the column to which the blob belongs
341+ * @return parsed value
342+ */
343+ private String parseBlobValue (InputStream inputStream , String columnName ) {
344+ if (inputStream == null ) {
345+ return null ;
346+ }
347+ try {
348+ ByteArrayOutputStream result = new ByteArrayOutputStream ();
349+ byte [] buffer = new byte [1024 ];
350+ int length ;
351+ while ((length = inputStream .read (buffer )) != -1 ) {
352+ result .write (buffer , 0 , length );
353+ }
354+ byte [] bytes = Base64 .encodeBase64 (result .toByteArray ());
355+ return new String (bytes , StandardCharsets .UTF_8 );
356+ } catch (IOException ex ) {
357+ log .error ("Unable to read BLOB for column {}" , columnName );
358+ throw new InternalErrorException ("Unable to read BLOB data for column: " + columnName , ex );
359+ }
360+ }
361+
362+ /**
363+ * Read data from rs from specified column. If the column doesn't exist,
364+ * a null is returned.
365+ *
366+ * @param rs result rest
367+ * @param column column
368+ * @return column data or null if column doesn't exist
369+ */
370+ private String readGroupSyncRequiredData (ResultSet rs , String column ) {
371+ try {
372+ return rs .getString (column );
373+ } catch (SQLException e ) {
374+ // If the column doesn't exist, ignore it
375+ return null ;
376+ }
377+ }
378+
290379}
0 commit comments