/*
jBilling - The Enterprise Open Source Billing System
Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde
This file is part of jbilling.
jbilling is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jbilling 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with jbilling. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sapienter.jbilling.server.payment.blacklist;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
import org.apache.log4j.Logger;
import com.sapienter.jbilling.common.SessionInternalError;
import com.sapienter.jbilling.server.payment.blacklist.db.BlacklistDAS;
import com.sapienter.jbilling.server.payment.blacklist.db.BlacklistDTO;
import com.sapienter.jbilling.server.user.EntityBL;
import com.sapienter.jbilling.server.user.contact.db.ContactDAS;
import com.sapienter.jbilling.server.user.contact.db.ContactDTO;
import com.sapienter.jbilling.server.user.contact.db.ContactFieldDTO;
import com.sapienter.jbilling.server.user.contact.db.ContactFieldTypeDAS;
import com.sapienter.jbilling.server.user.contact.db.ContactFieldTypeDTO;
import com.sapienter.jbilling.server.user.db.CompanyDAS;
import com.sapienter.jbilling.server.user.db.CompanyDTO;
import com.sapienter.jbilling.server.user.db.CreditCardDAS;
import com.sapienter.jbilling.server.user.db.CreditCardDTO;
import com.sapienter.jbilling.server.user.db.UserDAS;
import com.sapienter.jbilling.server.user.db.UserDTO;
import com.sapienter.jbilling.server.util.Util;
/**
* Processes blacklist CSV files.
* It either adds to or replaces any existing CSV created blacklist entries.
*/
public class CsvProcessor {
/**
* ParseException thrown when unexpected or missing data encountered.
*/
public static class ParseException extends RuntimeException {
private int lineNum;
private Column column;
public ParseException(String message, int lineNum, Column column) {
super(message);
this.lineNum = lineNum;
this.column = column;
}
public int getLineNum() {
return lineNum;
}
public Column getColumn() {
return column;
}
}
// the columns and their order
public enum Column { TYPE, FIRST_NAME, LAST_NAME, ADDRESS_1, ADDRESS_2, CITY,
STATE_PROVINCE, POSTAL_CODE, COUNTRY_CODE, PHONE_COUNTRY_CODE,
PHONE_AREA_CODE, PHONE_NUMBER, IP_ADDRESS, CC_NUMBER, USER_ID }
private static final int NUM_OF_COLUMNS = 15;
private static final char FIELD_SEPARATOR = ',';
private static final String R_BUNDLE_KEY = "payment.blacklist.csv.";
private static Logger LOG = Logger.getLogger(CsvProcessor.class);
// some frequently used data classes
private BlacklistDAS blacklistDAS = null;
private UserDAS userDAS = null;
private ContactDAS contactDAS = null;
private CreditCardDAS creditCardDAS = null;
private ContactFieldTypeDTO ipAddressFieldTypeDTO = null;
private ResourceBundle rBundle = null;
// current line and line number
private String[] currentLine = null;
private int lineNum = 0;
public CsvProcessor() {
blacklistDAS = new BlacklistDAS();
userDAS = new UserDAS();
contactDAS = new ContactDAS();
creditCardDAS = new CreditCardDAS();
}
/**
* Does the processing.
* Reads the blacklist CSV file specified by filePath.
* It will either add to or replace the existing uploaded
* blacklist for the given entity (company). Returns the number
* of new blacklist entries created.
*/
public int process(String filePath, boolean replace, Integer entityId)
throws ParseException {
BufferedReader inFile = null;
String inLine = null;
lineNum = 0;
int entriesAdded = 0;
try {
inFile = new BufferedReader(new FileReader(filePath));
CompanyDTO company = new CompanyDAS().find(entityId);
ipAddressFieldTypeDTO = new ContactFieldTypeDAS().find(
BlacklistBL.getIpAddressCcfId(entityId));
EntityBL entity = new EntityBL(entityId);
Locale locale = entity.getLocale();
rBundle = ResourceBundle.getBundle("entityNotifications",
locale);
// first, delete any existing entries, if required
if (replace) {
int number = blacklistDAS.deleteSource(entityId,
BlacklistDTO.SOURCE_EXTERNAL_UPLOAD);
LOG.debug("Deleted " + number + " externally uploaded " +
"blacklist entries");
}
// loop through each line, determine its type and
// create an appropriate blacklist entry
while ((inLine = inFile.readLine()) != null) {
lineNum++;
// skip blank lines
if (inLine.equals("")) {
continue;
}
currentLine = Util.csvSplitLine(inLine, FIELD_SEPARATOR);
// check for correct number of columns
if (currentLine.length != NUM_OF_COLUMNS) {
throw new ParseException(getMessage("columns", lineNum,
NUM_OF_COLUMNS, currentLine.length), lineNum, null);
}
Integer type = getInt(Column.TYPE);
BlacklistDTO entry = new BlacklistDTO();
if (type.equals(BlacklistDTO.TYPE_USER_ID)) {
createUserRecord(entry);
} else if (type.equals(BlacklistDTO.TYPE_NAME)) {
createNameRecord(entry);
} else if (type.equals(BlacklistDTO.TYPE_CC_NUMBER)) {
createCcRecord(entry);
} else if (type.equals(BlacklistDTO.TYPE_ADDRESS)) {
createAddressRecord(entry);
} else if (type.equals(BlacklistDTO.TYPE_IP_ADDRESS)) {
createIpAddressRecord(entry);
} else if (type.equals(BlacklistDTO.TYPE_PHONE_NUMBER)) {
createPhoneRecord(entry);
} else {
throw new ParseException(lineColMessage(Column.TYPE,
"invalid_type", type), lineNum, Column.TYPE);
}
entry.setType(type);
entry.setSource(BlacklistDTO.SOURCE_EXTERNAL_UPLOAD);
entry.setCompany(company);
entry.setCreateDate(new Date());
blacklistDAS.save(entry);
entriesAdded++;
}
} catch (ParseException pe) {
throw pe;
} catch (Exception e) {
throw new SessionInternalError("Error while processing",
CsvProcessor.class, e);
} finally {
try {
if (inFile != null) {
inFile.close();
}
} catch (IOException ioe) {}
}
LOG.debug("Added " + entriesAdded + " blacklist entries");
return entriesAdded;
}
/**
* Creates a user blacklist entry.
*/
private void createUserRecord(BlacklistDTO entry) throws ParseException {
Integer userId = getInt(Column.USER_ID);
if (userId == null) {
throw new ParseException(lineColMessage(Column.USER_ID,
"empty_user_id"), lineNum, Column.USER_ID);
}
// try to get user
UserDTO user = userDAS.findNow(userId);
if (user == null) {
throw new ParseException(lineColMessage(Column.USER_ID,
"invalid_user_id", userId), lineNum, Column.USER_ID);
}
entry.setUser(user);
}
/**
* Creates a name blacklist entry.
*/
private void createNameRecord(BlacklistDTO entry) throws ParseException {
checkForEmptyRecord("NAME", Column.FIRST_NAME, Column.LAST_NAME);
ContactDTO newContact = new ContactDTO();
newContact.setCreateDate(new Date());
newContact.setDeleted(0);
newContact.setFirstName(getString(Column.FIRST_NAME));
newContact.setLastName(getString(Column.LAST_NAME));
entry.setContact(newContact);
}
/**
* Creates an address blacklist entry.
*/
private void createAddressRecord(BlacklistDTO entry) throws ParseException {
checkForEmptyRecord("ADDRESS", Column.ADDRESS_1, Column.ADDRESS_2, Column.CITY,
Column.STATE_PROVINCE, Column.POSTAL_CODE, Column.COUNTRY_CODE);
ContactDTO newContact = new ContactDTO();
newContact.setCreateDate(new Date());
newContact.setDeleted(0);
newContact.setAddress1(getString(Column.ADDRESS_1));
newContact.setAddress2(getString(Column.ADDRESS_2));
newContact.setCity(getString(Column.CITY));
newContact.setStateProvince(getString(Column.STATE_PROVINCE));
newContact.setPostalCode(getString(Column.POSTAL_CODE));
newContact.setCountryCode(getString(Column.COUNTRY_CODE));
entry.setContact(newContact);
}
/**
* Creates a phone blacklist entry.
*/
private void createPhoneRecord(BlacklistDTO entry) throws ParseException {
checkForEmptyRecord("PHONE_NUMBER", Column.PHONE_COUNTRY_CODE,
Column.PHONE_AREA_CODE, Column.PHONE_NUMBER);
ContactDTO newContact = new ContactDTO();
newContact.setCreateDate(new Date());
newContact.setDeleted(0);
newContact.setPhoneCountryCode(getInt(Column.PHONE_COUNTRY_CODE));
newContact.setPhoneAreaCode(getInt(Column.PHONE_AREA_CODE));
newContact.setPhoneNumber(getString(Column.PHONE_NUMBER));
entry.setContact(newContact);
}
/**
* Creates a credit card number blacklist entry.
*/
private void createCcRecord(BlacklistDTO entry) throws ParseException {
checkForEmptyRecord("CC_NUMBER", Column.CC_NUMBER);
CreditCardDTO creditCard = new CreditCardDTO();
creditCard.setNumber(getString(Column.CC_NUMBER));
creditCard.setDeleted(0);
creditCard.setCcType(2); // not null
creditCard.setCcExpiry(new Date()); // not null
entry.setCreditCard(creditCard);
}
/**
* Creates an ip address blacklist entry.
*/
private void createIpAddressRecord(BlacklistDTO entry) throws ParseException {
checkForEmptyRecord("IP_ADDRESS", Column.IP_ADDRESS);
ContactDTO newContact = new ContactDTO();
newContact.setCreateDate(new Date());
newContact.setDeleted(0);
ContactFieldDTO newField = new ContactFieldDTO();
newField.setType(ipAddressFieldTypeDTO);
newField.setContent(getString(Column.IP_ADDRESS));
newField.setContact(newContact);
Set<ContactFieldDTO> fields = new HashSet<ContactFieldDTO>(1);
fields.add(newField);
newContact.setFields(fields);
entry.setContact(newContact);
}
/**
* Returns the data in the specified column as an Integer.
*/
private Integer getInt(Column column) throws ParseException {
String field = getString(column);
if (field == null) {
return null;
}
Integer integer = null;
try {
integer = new Integer(field);
} catch (NumberFormatException nfe) {
throw new ParseException(lineColMessage(column, "get_int", field),
lineNum, column);
}
return integer;
}
/**
* If the specified column contains an empty string,
* returns null, otherwise returns the string.
*/
private String getString(Column column) {
if (currentLine[column.ordinal()].equals("")) {
return null;
} else {
return currentLine[column.ordinal()];
}
}
/**
* Checks that all the given Columns aren't empty.
*/
private void checkForEmptyRecord(String type, Column... columns)
throws ParseException {
for (Column column : columns) {
if (!currentLine[column.ordinal()].equals("")) {
return;
}
}
throw new ParseException(getMessage("empty_record", lineNum, type),
lineNum, null);
}
/**
* Returns current location concatenated with the resource bundle message -
* useful for error messages.
*/
private String lineColMessage(Column column, String messageKey,
Object... messageParams) {
return getMessage("location", lineNum, column) +
getMessage(messageKey, messageParams);
}
/**
* Gets the international error message for the given key.
* Also inserts the parameters into the string.
*/
private String getMessage(String key, Object... params) {
String message = rBundle.getString(R_BUNDLE_KEY + key);
for (Object param : params) {
message = message.replaceFirst("\\|X\\|", param.toString());
}
return message;
}
}