001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.contentio.synchronize.impl; 017 018import java.util.HashMap; 019import java.util.LinkedHashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.collections4.CollectionUtils; 028import org.slf4j.Logger; 029 030import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollection; 031 032/** 033 * Implementation of {@link SynchronizableContentsCollection} to be synchronized with a LDAP data source 034 */ 035public class SQLSynchronizableContentsCollection extends AbstractDataSourceSynchronizableContentsCollection implements Component 036{ 037 private static final String __PARAM_SQL_TABLE = "tableName"; 038 039 /** The SQL collection DAO */ 040 protected SQLCollectionDAO _sqlCollectionDAO; 041 042 @Override 043 public void service(ServiceManager smanager) throws ServiceException 044 { 045 super.service(smanager); 046 _sqlCollectionDAO = (SQLCollectionDAO) smanager.lookup(SQLCollectionDAO.ROLE); 047 } 048 049 /** 050 * Get the name of SQL table 051 * @return The SQL table name 052 */ 053 public String getTableName() 054 { 055 return (String) getParameterValues().get(__PARAM_SQL_TABLE); 056 } 057 058 @Override 059 protected Map<String, Map<String, Object>> internalSearch(Map<String, Object> searchParameters, int offset, int limit, List<Object> sort, Logger logger) 060 { 061 Map<String, Map<String, Object>> result = new LinkedHashMap<>(); 062 063 Map<String, List<String>> mapping = getMapping(); 064 String idField = getIdField(); 065 List<String> remoteKeys = mapping.get(idField); 066 if (CollectionUtils.isNotEmpty(remoteKeys)) 067 { 068 String remoteKey = remoteKeys.get(0); 069 070 List<String> columns = mapping.values() 071 .stream() 072 .flatMap(List::stream) 073 .collect(Collectors.toList()); 074 075 Map<String, Object> params = _getSearchParameters(searchParameters, offset, limit, sort, columns); 076 List<Map<String, Object>> searchResults = _sqlCollectionDAO.search(params , getDataSourceId()); 077 result = _normalizeSearchResult(remoteKey, columns, searchResults, logger); 078 } 079 else 080 { 081 _nbError++; 082 logger.error("Missing SQL attribute in mapping for field '{}' holding the unique identifier", idField); 083 } 084 085 return result; 086 } 087 088 /** 089 * Get the parameters map for mybatis search 090 * @param parameters the filter parameter 091 * @param offset the offset 092 * @param limit the limit 093 * @param sort the sort map 094 * @param columns the list of columns 095 * @return the parameter map 096 */ 097 protected Map<String, Object> _getSearchParameters(Map<String, Object> parameters, int offset, int limit, List<Object> sort, List<String> columns) 098 { 099 Map<String, Object> params = new HashMap<>(); 100 params.put("table", getTableName()); 101 params.put("columns", columns); 102 params.put("params", parameters); 103 104 if (offset > 0) 105 { 106 params.put("offset", offset); 107 } 108 109 if (limit < Integer.MAX_VALUE) 110 { 111 params.put("limit", limit); 112 } 113 114 if (CollectionUtils.isNotEmpty(sort)) 115 { 116 params.put("sorts", sort); 117 } 118 119 return params; 120 } 121 122 /** 123 * We need to normalize the search result for each database type 124 * For example, with Oracle, the returned SQL column names are upper-cased 125 * so we need to retrieve for each returned column its real name for the mapping, 126 * @param remoteKey the remote key handling the id 127 * @param columns the list of columns 128 * @param searchResults the search result map 129 * @param logger the logger 130 * @return the normalize search result map 131 */ 132 protected Map<String, Map<String, Object>> _normalizeSearchResult(String remoteKey, List<String> columns, List<Map<String, Object>> searchResults, Logger logger) 133 { 134 Map<String, Map<String, Object>> result = new LinkedHashMap<>(); 135 136 for (Map<String, Object> searchResult : searchResults) 137 { 138 Map<String, Object> newSearchResult = _getNormalizedSearchResult(columns, searchResult); 139 140 Object idObjectValue = newSearchResult.get(remoteKey); 141 if (_checkIdObjectValue(remoteKey, idObjectValue, logger)) 142 { 143 newSearchResult.put(SCC_UNIQUE_ID, idObjectValue.toString()); 144 newSearchResult.put(remoteKey, idObjectValue.toString()); 145 result.put(idObjectValue.toString(), newSearchResult); 146 } 147 } 148 149 return result; 150 } 151 152 /** 153 * Check if the id object value is correct 154 * @param remoteKey the remove key 155 * @param idObjectValue the id object value 156 * @param logger the logger 157 * @return true if the id object value is correct 158 */ 159 protected boolean _checkIdObjectValue(String remoteKey, Object idObjectValue, Logger logger) 160 { 161 if (idObjectValue == null) 162 { 163 logger.warn("The content identifier is mandatory but there is no value for the key : " + remoteKey); 164 return false; 165 } 166 167 return true; 168 } 169 170 /** 171 * Get normalized search result 172 * Indeed we need to normalize the search result for each database type 173 * For example, with Oracle, the returned SQL column names are uppercased 174 * so we need to retrieve for each returned column its real name for the mapping, 175 * @param columns the columns list 176 * @param searchResult the search result 177 * @return the normalized search result 178 */ 179 protected Map<String, Object> _getNormalizedSearchResult(List<String> columns, Map<String, Object> searchResult) 180 { 181 Map<String, Object> normalizedSearchResult = new HashMap<>(); 182 for (String key : searchResult.keySet()) 183 { 184 //We search the initial column name 185 List<String> filteredValue = columns.stream() 186 .filter(c -> c.equalsIgnoreCase(key)) 187 .collect(Collectors.toList()); 188 if (!filteredValue.isEmpty()) //If we find no column, we ignore it (for example Oracle send 'Line' value) 189 { 190 normalizedSearchResult.put(filteredValue.get(0), searchResult.get(key)); 191 } 192 } 193 return normalizedSearchResult; 194 } 195 196 @Override 197 public int getTotalCount(Map<String, Object> parameters, Logger logger) 198 { 199 Map<String, Object> params = _getTotalCounParameters(parameters); 200 return _sqlCollectionDAO.getTotalCount(params , getDataSourceId()); 201 } 202 203 /** 204 * Get the parameters map for mybatis total count 205 * @param parameters the filter parameter 206 * @return the parameter map 207 */ 208 protected Map<String, Object> _getTotalCounParameters(Map<String, Object> parameters) 209 { 210 Map<String, Object> params = new HashMap<>(); 211 params.put("table", getTableName()); 212 params.put("params", _removeEmptyParameters(parameters)); 213 214 return params; 215 } 216 217}