/**Copyright 2010 Research Studios Austria Forschungsgesellschaft mBH * * This file is part of easyrec. * * easyrec is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * easyrec is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with easyrec. If not, see <http://www.gnu.org/licenses/>. */ package org.easyrec.service.core.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easyrec.model.core.ActionVO; import org.easyrec.model.core.ItemVO; import org.easyrec.model.core.transfer.TimeConstraintVO; import org.easyrec.service.core.ActionService; import org.easyrec.store.dao.core.ActionDAO; import org.easyrec.utils.io.autoimport.AutoImportService; import org.easyrec.utils.io.autoimport.AutoImportUtils; import java.io.*; import java.util.Iterator; import java.util.List; /** * Implementation of the {@link org.easyrec.service.core.ActionService} interface. * <p/> * <p><b>Company: </b> * SAT, Research Studios Austria</p> * <p/> * <p><b>Copyright: </b> * (c) 2007</p> * <p/> * <p><b>last modified:</b><br/> * $Author: dmann $<br/> * $Date: 2011-12-20 15:22:22 +0100 (Di, 20 Dez 2011) $<br/> * $Revision: 18685 $</p> * * @author Roman Cerny */ public class ActionServiceImpl implements ActionService { ////////////////////////////////////////////////////////////////////////////// // constants // logging private final Log logger = LogFactory.getLog(this.getClass()); ////////////////////////////////////////////////////////////////////////////// // members private ActionDAO actionDAO; // CSV import private int reportBlockSize = AutoImportService.DEFAULT__REPORT__BLOCK_SIZE; public ActionServiceImpl(ActionDAO actionDAO) { this.actionDAO = actionDAO; } // interface "ActionService" implementation public Iterator<ActionVO<Integer, Integer>> getActionIterator(int bulkSize) { return actionDAO.getActionIterator(bulkSize); } public Iterator<ActionVO<Integer, Integer>> getActionIterator(int bulkSize, TimeConstraintVO timeConstraint) { return actionDAO.getActionIterator(bulkSize, timeConstraint); } public List<ActionVO<Integer, Integer>> getActionsFromUser(Integer tenantId, Integer userId, String sessionId) { return actionDAO.getActionsFromUser(tenantId, userId, sessionId); } public int insertAction(ActionVO<Integer, Integer> action) { return actionDAO.insertAction(action, false); } public int insertAction(ActionVO<Integer, Integer> action, boolean useDateFromVO) { return actionDAO.insertAction(action, useDateFromVO); } public int removeActionsByTenant(Integer tenantId) { return actionDAO.removeActionsByTenant(tenantId); } public List<ItemVO<Integer, Integer>> getItemsOfTenant(final Integer tenant, final Integer consideredItemType) { return actionDAO.getItemsOfTenant(tenant, consideredItemType); } public List<ItemVO<Integer, Integer>> getItemsByUserActionAndType(Integer tenantId, Integer userId, String sessionId, Integer consideredActionType, Integer consideredItemType, Double ratingThreshold, Integer numberOfLastActionsConsidered) { return actionDAO .getItemsByUserActionAndType(tenantId, userId, sessionId, consideredActionType, consideredItemType, ratingThreshold, numberOfLastActionsConsidered); } public void importActionsFromCSV(String fileName) { importActionsFromCSV(fileName, null); } public void importActionsFromCSV(String fileName, ActionVO<Integer, Integer> defaults) { long start = System.currentTimeMillis(); if (logger.isInfoEnabled()) { logger.info("==================== starting importing 'action' ======================="); logger.info("importing 'action' from CSV '" + fileName + "'"); logger.info("using interface defaults '" + defaults + "'"); logger.info("========================================================================"); } if (fileName == null) { throw new IllegalArgumentException("missing 'fileName'"); } BufferedReader br = null; int lineCounter = 3; int savedCounter = 0; int removedCounter = 0; int skippedCounter = 0; int errorCounter = 0; int currentSavedCounter = 0; String command = null; try { try { br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName))); } catch (FileNotFoundException e) { throw new IllegalArgumentException("file '" + fileName + "' not found", e); } String line = null; String elementsOfLine[] = null; // read command try { line = br.readLine(); } catch (IOException e) { throw new IllegalStateException("unexpected IOException", e); } try { // in case of we have an 'old style' file format, containing no 'type' definition, then the first line would contain the command command = AutoImportUtils.retrieveCommandFromLine(line); } catch (IllegalArgumentException e) { // this should be the normal case, the file contains a 'type' definition, so we need to skip this line // read/skip type try { line = br.readLine(); } catch (IOException ioe) { throw new IllegalStateException("unexpected IOException", ioe); } command = AutoImportUtils.retrieveCommandFromLine(line); } // skip file if the command is not an 'insert' command if (!AutoImportUtils.COMMAND_INSERT.equalsIgnoreCase(command)) { throw new IllegalStateException("command '" + command + "' is not allowed for the type 'action'"); } // read header and generate headerDefaults try { line = br.readLine(); } catch (IOException e) { throw new IllegalStateException("unexpected IOException", e); } ActionVO<Integer, Integer> headerDefaults = generateDefaultsFromHeader(line, defaults); if (logger.isInfoEnabled()) { logger.info("extracted header defaults from csv file, using: " + headerDefaults); } // fetch next line try { while ((line = br.readLine()) != null) { lineCounter++; // skip empty lines if ("".equals(line)) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "line is empty"); skippedCounter++; continue; } // skip comment lines if (line.startsWith(Character.toString(AutoImportUtils.CSV_COMMENT_CHAR))) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "line is a comment"); skippedCounter++; continue; } // skip lines, with wrong number of columns elementsOfLine = line.split(AutoImportUtils.CSV_SEPARATOR, ActionVO.CSV_NUMBER_OF_COLUMNS); if (elementsOfLine.length != ActionVO.CSV_NUMBER_OF_COLUMNS) { StringBuilder s = new StringBuilder("', number of columns should be '"); s.append(ActionVO.CSV_NUMBER_OF_COLUMNS); s.append("', but was '"); s.append(elementsOfLine.length); s.append("'"); AutoImportUtils.logSkippedLine(logger, lineCounter, line, s.toString()); skippedCounter++; continue; } ActionVO<Integer, Integer> action = null; try { action = (ActionVO<Integer, Integer>) headerDefaults.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException( "value object 'ActionVO' does not support .clone() anymore, check that!!"); } // parse 'tenantId' if (!"".equals(elementsOfLine[0])) { try { action.setTenant(Integer.parseInt(elementsOfLine[0])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'tenantId' is no valid 'Integer'"); skippedCounter++; continue; } } else { // 'tenantId' NOT NULL if (action.getTenant() == null) { AutoImportUtils .logSkippedLine(logger, lineCounter, line, "no value for field 'tenantId' is set"); skippedCounter++; continue; } } // parse 'userId' if (!"".equals(elementsOfLine[1])) { try { action.setUser(Integer.parseInt(elementsOfLine[1])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'userId' is no valid 'Integer'"); skippedCounter++; continue; } } // parse 'sessionId' if (!"".equals(elementsOfLine[2])) { action.setSessionId(elementsOfLine[2]); } // parse 'ip' if (!"".equals(elementsOfLine[3])) { action.setIp(elementsOfLine[3]); } // parse 'itemId' if (!"".equals(elementsOfLine[4])) { try { action.getItem().setItem(Integer.parseInt(elementsOfLine[4])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'itemId' is no valid 'Integer'"); skippedCounter++; continue; } } // parse 'itemTypeId' if (!"".equals(elementsOfLine[5])) { try { action.getItem().setType(Integer.parseInt(elementsOfLine[5])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'itemTypeId' is no valid 'Integer'"); skippedCounter++; continue; } } else { // 'itemTypeId' NOT NULL if (action.getItem().getType() == null) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "no value for field 'itemTypeId' is set"); skippedCounter++; continue; } } // parse 'actionTypeId' if (!"".equals(elementsOfLine[6])) { try { action.setActionType(Integer.parseInt(elementsOfLine[6])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'actionTypeId' is no valid 'Integer'"); skippedCounter++; continue; } } else { // 'actionTypeId' NOT NULL if (action.getActionType() == null) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "no value for field 'actionTypeId' is set"); skippedCounter++; continue; } } // parse 'ratingValue' if (!"".equals(elementsOfLine[7])) { try { action.setRatingValue(Integer.parseInt(elementsOfLine[7])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'ratingValue' is no valid 'Integer'"); skippedCounter++; continue; } } // parse 'searchSucceeded' if (!"".equals(elementsOfLine[8])) { action.setSearchSucceeded(Boolean.parseBoolean(elementsOfLine[8])); } // parse 'numberOfFoundItems' if (!"".equals(elementsOfLine[9])) { try { action.setNumberOfFoundItems(Integer.parseInt(elementsOfLine[9])); } catch (NumberFormatException nfe) { AutoImportUtils.logSkippedLine(logger, lineCounter, line, "value for field 'numberOfFoundItems' is no valid 'Integer'"); skippedCounter++; continue; } } // parse 'description' if (!"".equals(elementsOfLine[10])) { action.setDescription(elementsOfLine[10]); } try { int savedThisIteration = 0; // inserting action (no update allowed for type 'action') savedThisIteration = insertAction(action); currentSavedCounter += savedThisIteration; savedCounter += savedThisIteration; int currentSavedMultiplier = currentSavedCounter / reportBlockSize; if (currentSavedMultiplier > 0) { if (logger.isInfoEnabled()) { logger.info("number of saved 'action' entries: " + (currentSavedMultiplier * reportBlockSize)); } currentSavedCounter %= reportBlockSize; } /*} catch (DataIntegrityViolationException dive) { // wait a little, we have two identical actions, without an actionDate try { Thread.sleep(1000); } catch (InterruptedException ie) { logger.info("error occured during (delayed try of) insertAction() '" + action + "'", ie); } int savedThisIteration = 0; // inserting action (no update allowed for type 'action') savedThisIteration = insertAction(action); currentSavedCounter += savedThisIteration; savedCounter += savedThisIteration; int currentSavedMultiplier = currentSavedCounter / reportBlockSize; if (currentSavedMultiplier > 0) { if (logger.isInfoEnabled()) { logger.info("number of saved 'action' entries: " + (currentSavedMultiplier * reportBlockSize)); } currentSavedCounter %= reportBlockSize; }*/ } catch (Exception e) { errorCounter++; logger.error("error occured during insertAction() '" + action + "'", e); } } // end of while } catch (IOException e) { throw new IllegalStateException("unexpected IOException", e); } } finally { // close stream if (br != null) { try { br.close(); } catch (IOException e) { throw new IllegalStateException("unexpected IOException", e); } } } if (logger.isInfoEnabled()) { if (currentSavedCounter > 0) { logger.info("number of saved 'action' entries: " + currentSavedCounter); } logger.info("==================== finished importing from file '" + fileName + "' ===================="); logger.info("total number of saved 'action' entries: " + savedCounter); logger.info("total number of removed 'action' entries: " + removedCounter); logger.info("total number of skipped 'action' entries: " + skippedCounter); logger.info("total number of errors occured while import: " + errorCounter); logger.info("used defaults: '" + defaults + "'"); logger.info("time taken: " + (System.currentTimeMillis() - start) + " ms"); logger.info("========================================================================"); } } ////////////////////////////////////////////////////////////////////////////// // private methods private ActionVO<Integer, Integer> generateDefaultsFromHeader(String header, ActionVO<Integer, Integer> defaults) throws IllegalArgumentException { String[] elementsOfHeader = header.split(AutoImportUtils.CSV_SEPARATOR, ActionVO.CSV_NUMBER_OF_COLUMNS); if (elementsOfHeader.length != ActionVO.CSV_NUMBER_OF_COLUMNS) { throw new IllegalArgumentException( "the number of columns in the header of an 'action' .CSV file must be '" + ActionVO.CSV_NUMBER_OF_COLUMNS + "', but was '" + elementsOfHeader.length + "'"); } ActionVO<Integer, Integer> action; if (defaults != null) { try { action = (ActionVO<Integer, Integer>) defaults.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException( "value object 'ActionVO' does not support .clone() anymore, check that!!"); } } else { action = new ActionVO<Integer, Integer>(null, null, null, null, new ItemVO<Integer, Integer>(null, null, null), null, null, null, null, null); } // parse 'tenantId' String defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[0]); if (defaultValue != null) { try { action.setTenant(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn("the default value for 'tenantId' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'userId' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[1]); if (defaultValue != null) { try { action.setUser(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn("the default value for 'userId' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'sessionId' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[2]); if (defaultValue != null) { action.setSessionId(defaultValue); } // parse 'ip' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[3]); if (defaultValue != null) { action.setIp(defaultValue); } // parse 'itemId' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[4]); if (defaultValue != null) { try { action.getItem().setItem(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn("the default value for 'itemId' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'itemTypeId' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[5]); if (defaultValue != null) { try { action.getItem().setType(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn( "the default value for 'itemTypeId' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'actionTypeId' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[6]); if (defaultValue != null) { try { action.setActionType(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn( "the default value for 'actionTypeId' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'ratingValue' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[7]); if (defaultValue != null) { try { action.setRatingValue(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn( "the default value for 'ratingValue' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'searchSucceeded' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[8]); if (defaultValue != null) { action.setSearchSucceeded(Boolean.parseBoolean(defaultValue)); } // parse 'numberOfFoundItems' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[9]); if (defaultValue != null) { try { action.setNumberOfFoundItems(Integer.parseInt(defaultValue)); } catch (NumberFormatException nfe) { logger.warn( "the default value for 'numberOfFoundItems' in the CSV header is no valid 'Integer', passed type='" + defaultValue + "'; the passed type will be ignored!"); } } // parse 'description' defaultValue = AutoImportUtils.getDefaultFromHeaderPart(elementsOfHeader[10]); if (defaultValue != null) { action.setDescription(defaultValue); } return action; } }