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