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> parameters, 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(parameters, 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 uppercased 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 result.put(idObjectValue.toString(), newSearchResult); 145 } 146 } 147 148 return result; 149 } 150 151 /** 152 * Check if the id object value is correct 153 * @param remoteKey the remove key 154 * @param idObjectValue the id object value 155 * @param logger the logger 156 * @return true if the id object value is correct 157 */ 158 protected boolean _checkIdObjectValue(String remoteKey, Object idObjectValue, Logger logger) 159 { 160 if (idObjectValue == null) 161 { 162 logger.warn("The content identifier is mandatory but there is no value for the key : " + remoteKey); 163 return false; 164 } 165 166 return true; 167 } 168 169 /** 170 * Get normalized search result 171 * Indeed we need to normalize the search result for each database type 172 * For example, with Oracle, the returned SQL column names are uppercased 173 * so we need to retrieve for each returned column its real name for the mapping, 174 * @param columns the columns list 175 * @param searchResult the search result 176 * @return the normalized search result 177 */ 178 protected Map<String, Object> _getNormalizedSearchResult(List<String> columns, Map<String, Object> searchResult) 179 { 180 Map<String, Object> normalizedSearchResult = new HashMap<>(); 181 for (String key : searchResult.keySet()) 182 { 183 //We search the initial column name 184 List<String> filteredValue = columns.stream() 185 .filter(c -> c.equalsIgnoreCase(key)) 186 .collect(Collectors.toList()); 187 if (!filteredValue.isEmpty()) //If we find no column, we ignore it (for example Oracle send 'Line' value) 188 { 189 normalizedSearchResult.put(filteredValue.get(0), searchResult.get(key)); 190 } 191 } 192 return normalizedSearchResult; 193 } 194 195 @Override 196 public int getTotalCount(Map<String, Object> parameters, Logger logger) 197 { 198 Map<String, Object> params = _getTotalCounParameters(parameters); 199 return _sqlCollectionDAO.getTotalCount(params , getDataSourceId()); 200 } 201 202 /** 203 * Get the parameters map for mybatis total count 204 * @param parameters the filter parameter 205 * @return the parameter map 206 */ 207 protected Map<String, Object> _getTotalCounParameters(Map<String, Object> parameters) 208 { 209 Map<String, Object> params = new HashMap<>(); 210 params.put("table", getTableName()); 211 params.put("params", _removeEmptyParameters(parameters)); 212 213 return params; 214 } 215 216}