001/* 002 * Copyright 2015 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.export.sql; 017 018import java.io.IOException; 019import java.sql.SQLException; 020import java.util.HashMap; 021import java.util.Map; 022 023import org.apache.avalon.framework.component.Component; 024import org.apache.avalon.framework.configuration.Configurable; 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.context.ContextException; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang.StringUtils; 032 033import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 034import org.ametys.core.util.I18nUtils; 035import org.ametys.plugins.repository.AmetysRepositoryException; 036import org.ametys.runtime.i18n.I18nizableText; 037import org.ametys.runtime.plugin.component.AbstractLogEnabled; 038 039/** 040 * This component handles content export in database 041 */ 042public class ExportManager extends AbstractLogEnabled implements Configurable, Component, Serviceable 043{ 044 /** The component role */ 045 public static final String ROLE = ExportManager.class.getName(); 046 047 /** The default prefix */ 048 public static final String DEFAULT_TABLE_PREFIX = "AmetysExport"; 049 050 /** Connection pool name */ 051 public static final String CONTENT_EXPORT_POOL_NAME = "content.export.jdbc.pool"; 052 053 /** The name of the table for datas from rich text */ 054 public static final String RICH_TEXT_DATA_TABLE_NAME = "Ametys_RichTextImages"; 055 056 /** The name of the table for mapping table name */ 057 public static final String MAPPING_TABLE_NAME = "Ametys_tableMapping"; 058 059 /** The name of the table for mapping column name */ 060 public static final String MAPPING_COLUMN_NAME = "Ametys_columnMapping"; 061 062 /** The name of the table for mapping column name */ 063 public static final String CONTENT_TABLE_NAME = "Ametys_AllContents"; 064 065 /** The default separator */ 066 public static final String DEFAULT_SEPARATOR = ", "; 067 068 /** Content type extension point. */ 069 protected ContentTypeExtensionPoint _contentTypeExtensionPoint; 070 071 /** The i18n translator. */ 072 protected I18nUtils _i18nTranslator; 073 074 /** The delete sql table component. */ 075 protected DeleteSqlTableComponent _deleteSqlTableComponent; 076 077 /** The create sql table component. */ 078 protected CreateSqlTableComponent _createSqlTableComponent; 079 080 /** The fill sql table component. */ 081 protected FillSqlTableComponent _fillSqlTableComponent; 082 083 private ExportConfiguration _exportConfiguration; 084 085 @Override 086 public void service(ServiceManager sManager) throws ServiceException 087 { 088 _contentTypeExtensionPoint = (ContentTypeExtensionPoint) sManager.lookup(ContentTypeExtensionPoint.ROLE); 089 _i18nTranslator = (I18nUtils) sManager.lookup(I18nUtils.ROLE); 090 _deleteSqlTableComponent = (DeleteSqlTableComponent) sManager.lookup(DeleteSqlTableComponent.ROLE); 091 _createSqlTableComponent = (CreateSqlTableComponent) sManager.lookup(CreateSqlTableComponent.ROLE); 092 _fillSqlTableComponent = (FillSqlTableComponent) sManager.lookup(FillSqlTableComponent.ROLE); 093 } 094 095 @Override 096 public void configure(Configuration configuration) throws ConfigurationException 097 { 098 _exportConfiguration = new ExportConfiguration(); 099 100 // Set the table prefix 101 String tablePrefix = configuration.getAttribute("prefix", DEFAULT_TABLE_PREFIX); 102 if (StringUtils.isNotEmpty(tablePrefix) && !StringUtils.endsWith(tablePrefix, "_")) 103 { 104 tablePrefix += "_"; 105 } 106 _exportConfiguration.setTablePrefix(tablePrefix); 107 108 String exportOnlyValidatedContentAsString = configuration.getAttribute("exportOnlyValidatedContent", "false"); 109 _exportConfiguration.setExportOnlyValidatedContent("true".equals(exportOnlyValidatedContentAsString)); 110 111 String exportNoMultiValuedTableAsString = configuration.getAttribute("exportNoMultiValuedTable", "false"); 112 _exportConfiguration.setExportNoMultiValuedTable("true".equals(exportNoMultiValuedTableAsString)); 113 114 String separator = configuration.getAttribute("separator", DEFAULT_SEPARATOR); 115 _exportConfiguration.setSeparator(separator); 116 117 // Set the content type to export 118 _configureContentTypeToExport(configuration, tablePrefix); 119 120 // Set the sql mapping 121 _configureSQLMapping(configuration); 122 123 // Set the reserved words 124 _configureReservedWords(configuration); 125 126 // Set the mapping policy 127 String mappingPolicy = configuration.getChild("mapping-policy").getAttribute("value", "FULL"); 128 _exportConfiguration.setMappingPolicy(mappingPolicy); 129 } 130 131 /** 132 * Configure the content type to export 133 * @param configuration the configuration 134 * @param tablePrefix the table prefix 135 * @throws ConfigurationException if an error occurred 136 */ 137 protected void _configureContentTypeToExport(Configuration configuration, String tablePrefix) throws ConfigurationException 138 { 139 Map<String, String> contentTypesToExport = new HashMap<>(); 140 for (Configuration contentConf : configuration.getChild("contents").getChildren("content")) 141 { 142 String cTypeId = contentConf.getAttribute("id"); 143 String name = contentConf.getAttribute("name", null); 144 if (StringUtils.isNotBlank(name)) 145 { 146 contentTypesToExport.put(cTypeId, tablePrefix + name); 147 } 148 else 149 { 150 String defaultTableName = cTypeId.substring(cTypeId.lastIndexOf(".") + 1); 151 contentTypesToExport.put(cTypeId, tablePrefix + defaultTableName); 152 } 153 } 154 155 if (contentTypesToExport.isEmpty()) 156 { 157 for (String cTypeId : _contentTypeExtensionPoint.getExtensionsIds()) 158 { 159 String defaultTableName = cTypeId.substring(cTypeId.lastIndexOf(".") + 1); 160 contentTypesToExport.put(cTypeId, tablePrefix + defaultTableName); 161 } 162 } 163 164 _exportConfiguration.setContentTypesToExport(contentTypesToExport); 165 } 166 167 /** 168 * Configure the reserved words 169 * @param configuration the configuration 170 * @throws ConfigurationException if an error occurred 171 */ 172 protected void _configureReservedWords(Configuration configuration) throws ConfigurationException 173 { 174 boolean override = "true".equals(configuration.getChild("reserved-words").getAttribute("override", "false")); 175 176 Map<String, Map<String, String>> reservedWords = new HashMap<>(); 177 178 Configuration mysqlReservedWords = configuration.getChild("reserved-words").getChild("mysql"); 179 Map<String, String> mysqlReservedWordsMap = new HashMap<>(); 180 if (!override) 181 { 182 mysqlReservedWordsMap = ReservedWordsUtils.RESERVED_WORDS_MYSQL; 183 } 184 for (Configuration wordConf : mysqlReservedWords.getChildren("word")) 185 { 186 mysqlReservedWordsMap.put(wordConf.getAttribute("name"), wordConf.getAttribute("alias")); 187 } 188 if (mysqlReservedWordsMap.isEmpty()) 189 { 190 mysqlReservedWordsMap = ReservedWordsUtils.RESERVED_WORDS_MYSQL; 191 } 192 reservedWords.put(ReservedWordsUtils.MYSQL_KEY, mysqlReservedWordsMap); 193 194 Configuration oracleReservedWords = configuration.getChild("reserved-words").getChild("oracle"); 195 Map<String, String> oracleReservedWordsMap = new HashMap<>(); 196 if (!override) 197 { 198 oracleReservedWordsMap = ReservedWordsUtils.RESERVED_WORDS_ORACLE; 199 } 200 for (Configuration wordConf : oracleReservedWords.getChildren("word")) 201 { 202 oracleReservedWordsMap.put(wordConf.getAttribute("name"), wordConf.getAttribute("alias")); 203 } 204 if (oracleReservedWordsMap.isEmpty()) 205 { 206 oracleReservedWordsMap = ReservedWordsUtils.RESERVED_WORDS_ORACLE; 207 } 208 reservedWords.put(ReservedWordsUtils.ORACLE_KEY, oracleReservedWordsMap); 209 210 _exportConfiguration.setReservedWords(reservedWords); 211 } 212 213 /** 214 * Configure the sql mapping 215 * @param configuration the configuration 216 */ 217 protected void _configureSQLMapping(Configuration configuration) 218 { 219 Map<String, Map<String, String>> mappingSql = new HashMap<>(); 220 221 Map<String, String> mappingSqlMetadata = new HashMap<>(); 222 mappingSqlMetadata.put("short-string", configuration.getChild("sql").getChild("mysql").getChild("short-string").getValue("VARCHAR(255)")); 223 mappingSqlMetadata.put("string", configuration.getChild("sql").getChild("mysql").getChild("string").getValue("TEXT")); 224 mappingSqlMetadata.put("long", configuration.getChild("sql").getChild("mysql").getChild("long").getValue("INT")); 225 mappingSqlMetadata.put("boolean", configuration.getChild("sql").getChild("mysql").getChild("boolean").getValue("TINYINT")); 226 mappingSqlMetadata.put("date", configuration.getChild("sql").getChild("mysql").getChild("date").getValue("DATE")); 227 mappingSqlMetadata.put("datetime", configuration.getChild("sql").getChild("mysql").getChild("datetime").getValue("DATETIME")); 228 mappingSqlMetadata.put("double", configuration.getChild("sql").getChild("mysql").getChild("double").getValue("DOUBLE")); 229 mappingSqlMetadata.put("rich-text", configuration.getChild("sql").getChild("mysql").getChild("rich-text").getValue("MEDIUMTEXT")); 230 mappingSqlMetadata.put("content", configuration.getChild("sql").getChild("mysql").getChild("content").getValue("VARCHAR(512)")); 231 mappingSqlMetadata.put("file", configuration.getChild("sql").getChild("mysql").getChild("file").getValue("LONGBLOB")); 232 mappingSqlMetadata.put("binary", configuration.getChild("sql").getChild("mysql").getChild("binary").getValue("LONGBLOB")); 233 mappingSql.put("mysql", mappingSqlMetadata); 234 235 Map<String, String> mappingOracleMetadata = new HashMap<>(); 236 mappingOracleMetadata.put("short-string", configuration.getChild("sql").getChild("mysql").getChild("short-string").getValue("VARCHAR(255)")); 237 mappingOracleMetadata.put("string", configuration.getChild("sql").getChild("oracle").getChild("string").getValue("VARCHAR(4000)")); 238 mappingOracleMetadata.put("long", configuration.getChild("sql").getChild("oracle").getChild("long").getValue("NUMBER")); 239 mappingOracleMetadata.put("boolean", configuration.getChild("sql").getChild("oracle").getChild("boolean").getValue("CHAR(1)")); 240 mappingOracleMetadata.put("date", configuration.getChild("sql").getChild("oracle").getChild("date").getValue("DATE")); 241 mappingOracleMetadata.put("datetime", configuration.getChild("sql").getChild("oracle").getChild("datetime").getValue("DATE")); 242 mappingOracleMetadata.put("double", configuration.getChild("sql").getChild("oracle").getChild("double").getValue("NUMBER")); 243 mappingOracleMetadata.put("rich-text", configuration.getChild("sql").getChild("oracle").getChild("rich-text").getValue("VARCHAR(4000)")); 244 mappingOracleMetadata.put("content", configuration.getChild("sql").getChild("oracle").getChild("content").getValue("VARCHAR(512)")); 245 mappingOracleMetadata.put("file", configuration.getChild("sql").getChild("oracle").getChild("file").getValue("BLOB")); 246 mappingOracleMetadata.put("binary", configuration.getChild("sql").getChild("oracle").getChild("binary").getValue("BLOB")); 247 mappingSql.put("oracle", mappingOracleMetadata); 248 249 _exportConfiguration.setMappingSql(mappingSql); 250 } 251 252 /** 253 * Export the configured content types to the CONTENT_EXPORT_POOL_NAME db pool 254 * @throws SQLException If a sql error occurred 255 * @throws ContextException if a context error occurred 256 * @throws AmetysRepositoryException if a ametys repository error occurred 257 * @throws IOException if an IO error occurred 258 */ 259 public void export() throws SQLException, AmetysRepositoryException, ContextException, IOException 260 { 261 boolean isInfoEnabled = getLogger().isInfoEnabled(); 262 if (isInfoEnabled) 263 { 264 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_BEGIN"))); 265 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_INIT_BEGIN"))); 266 } 267 268 // Initialization 269 initialize(); 270 271 if (isInfoEnabled) 272 { 273 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_INIT_END"))); 274 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_DELETE_BEGIN"))); 275 } 276 277 // First delete all tables 278 _deleteSqlTableComponent.deleteExistingSqlTables(_exportConfiguration); 279 280 if (isInfoEnabled) 281 { 282 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_CREATE_BEGIN"))); 283 } 284 285 // Then create all tables 286 Map<String, ExportTableInfo> tableInfo = _createSqlTableComponent.createTables(_exportConfiguration); 287 288 if (isInfoEnabled) 289 { 290 getLogger().info(_i18nTranslator.translate(new I18nizableText("plugin.contentio", "PLUGINS_CONTENTIO_CONTENT_EXPORT_LOG_FILL_BEGIN"))); 291 } 292 293 // Finally insert values 294 _fillSqlTableComponent.fillTable(_exportConfiguration, tableInfo); 295 } 296 297 /** 298 * Initialization 299 */ 300 protected void initialize() 301 { 302 Map<String, String> contents = _exportConfiguration.getContentTypesToExport(); 303 304 if (contents.isEmpty()) 305 { 306 String prefix = _exportConfiguration.getTablePrefix(); 307 for (String contentTypeId : _contentTypeExtensionPoint.getExtensionsIds()) 308 { 309 String defaultTableName = prefix + contentTypeId.substring(contentTypeId.lastIndexOf(".") + 1); 310 contents.put(contentTypeId, defaultTableName); 311 } 312 313 _exportConfiguration.setContentTypesToExport(contents); 314 } 315 } 316 317 /** 318 * Get export configuration 319 * @return the export configuration 320 */ 321 public ExportConfiguration getExportConfiguration() 322 { 323 return _exportConfiguration; 324 } 325 326 /** 327 * Set export configuration 328 * @param exportConfiguration the export configuration 329 */ 330 public void setExportConfiguration(ExportConfiguration exportConfiguration) 331 { 332 this._exportConfiguration = exportConfiguration; 333 } 334}