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); 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 * @return the normalize search result map 130 */ 131 protected Map<String, Map<String, Object>> _normalizeSearchResult(String remoteKey, List<String> columns, List<Map<String, Object>> searchResults) 132 { 133 Map<String, Map<String, Object>> result = new LinkedHashMap<>(); 134 135 for (Map<String, Object> searchResult : searchResults) 136 { 137 Map<String, Object> newSearchResult = _getNormalizedSearchResult(columns, searchResult); 138 139 Object idObjectValue = newSearchResult.get(remoteKey); 140 newSearchResult.put(SCC_UNIQUE_ID, idObjectValue.toString()); 141 result.put(idObjectValue.toString(), newSearchResult); 142 } 143 144 return result; 145 } 146 147 /** 148 * Get normalized search result 149 * Indeed we need to normalize the search result for each database type 150 * For example, with Oracle, the returned SQL column names are uppercased 151 * so we need to retrieve for each returned column its real name for the mapping, 152 * @param columns the columns list 153 * @param searchResult the search result 154 * @return the normalized search result 155 */ 156 protected Map<String, Object> _getNormalizedSearchResult(List<String> columns, Map<String, Object> searchResult) 157 { 158 Map<String, Object> normalizedSearchResult = new HashMap<>(); 159 for (String key : searchResult.keySet()) 160 { 161 //We search the initial column name 162 List<String> filteredValue = columns.stream() 163 .filter(c -> c.equalsIgnoreCase(key)) 164 .collect(Collectors.toList()); 165 if (!filteredValue.isEmpty()) //If we find no column, we ignore it (for example Oracle send 'Line' value) 166 { 167 normalizedSearchResult.put(filteredValue.get(0), searchResult.get(key)); 168 } 169 } 170 return normalizedSearchResult; 171 } 172 173 @Override 174 public int getTotalCount(Map<String, Object> parameters, Logger logger) 175 { 176 Map<String, Object> params = _getTotalCounParameters(parameters); 177 return _sqlCollectionDAO.getTotalCount(params , getDataSourceId()); 178 } 179 180 /** 181 * Get the parameters map for mybatis total count 182 * @param parameters the filter parameter 183 * @return the parameter map 184 */ 185 protected Map<String, Object> _getTotalCounParameters(Map<String, Object> parameters) 186 { 187 Map<String, Object> params = new HashMap<>(); 188 params.put("table", getTableName()); 189 params.put("params", _removeEmptyParameters(parameters)); 190 191 return params; 192 } 193 194}