/**
 * Copyright 2008-2009 DRIVER PROJECT (ICM UW)
 * Original author: Marek Horst
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.dnetlib.data.index.ws.nh;

import java.io.IOException;
import java.io.StringReader;
import java.security.InvalidParameterException;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.xerces.parsers.SAXParser;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import eu.dnetlib.common.profile.BlackboardBody;
import eu.dnetlib.common.profile.IndexBody;
import eu.dnetlib.common.profile.IndexServiceBody;
import eu.dnetlib.common.profile.Profile;
import eu.dnetlib.common.profile.ProfileHeader;
import eu.dnetlib.common.profile.blackboard.IBlackboardMessage;
import eu.dnetlib.common.profile.blackboard.Message;
import eu.dnetlib.common.profile.blackboard.Parameter;
import eu.dnetlib.common.profile.blackboard.IBlackboardMessage.ActionStatus;
import eu.dnetlib.common.profile.utils.ProfileMarshaller;
import eu.dnetlib.common.ws.harv.parser.ContentParserContext;
import eu.dnetlib.common.ws.harv.parser.IDriverContentParserBuilder;
import eu.dnetlib.common.ws.nh.deleg.INotificationDelegatorClient;
import eu.dnetlib.common.ws.subscription.NotificationConstants;
import eu.dnetlib.data.index.IndexServiceException;
import eu.dnetlib.miscutils.datetime.DateUtils;
import eu.dnetlib.resultset.impl.builder.IResultSetBuilder;
import eu.dnetlib.data.index.utils.QueryProvider;
import eu.dnetlib.data.index.ws.commons.event.CreateIndexEvent;
import eu.dnetlib.data.index.ws.commons.event.DeleteIndexEvent;
import eu.dnetlib.data.index.ws.commons.event.FeedIndexEvent;
import eu.dnetlib.data.index.ws.commons.event.IGenericEvent;
import eu.dnetlib.data.index.ws.commons.event.IIndexEventProducer;
import eu.dnetlib.data.index.ws.commons.event.client.CreateIndexClient;
import eu.dnetlib.data.index.ws.commons.event.client.DeleteIndexClient;
import eu.dnetlib.data.index.ws.commons.event.client.FeedIndexClient;
import eu.dnetlib.data.index.ws.commons.event.dto.CreateIndexParamDTO;
import eu.dnetlib.data.index.ws.commons.event.dto.DeleteIndexParamDTO;
import eu.dnetlib.data.index.ws.commons.event.dto.FeedIndexParamDTO;
import eu.dnetlib.data.index.ws.commons.event.response.GenericEventResponse;
import eu.dnetlib.data.index.ws.commons.event.response.GenericEventResponse.Status;
import eu.dnetlib.data.index.ws.commons.profile.utils.IndexProfileMarshaller;
import eu.dnetlib.data.index.ws.commons.profile.utils.IndexProfileUnMarshaller;
import eu.dnetlib.data.index.ws.ctx.GlobalIndexContext;
import eu.dnetlib.data.index.ws.feed.FeedObject;
import eu.dnetlib.data.index.ws.feed.ResultSetFeedIterator;
import eu.dnetlib.data.index.ws.mdformat.DriverFieldSpec;
import eu.dnetlib.data.index.ws.mdformat.IMDFormatRelationsContainer;
import eu.dnetlib.data.index.ws.mdformat.MDFormatHandler;
import eu.dnetlib.data.index.ws.yadda.SearchModuleFacade;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpDocumentNotFoundException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.is.sn.rmi.ISSNService;

import sun.misc.BASE64Decoder;

/**
 * Index notification handler.
 * @author mhorst
 *
 */
public class IndexNotificationHandler implements INotificationDelegatorClient, IIndexEventProducer {

	protected static final Logger log = Logger.getLogger(IndexNotificationHandler.class);

	/**
	 * used by NotificationDelegator to pick supported notification consumer.
	 */
	public static final String TOPIC_REGEX_PATTERN = "("+NotificationConstants.TOPIC_PREFIX_CREATE+"\\.|"+
		NotificationConstants.TOPIC_PREFIX_DELETE+"\\.|"+
		NotificationConstants.TOPIC_PREFIX_UPDATE+"\\.)"+
		NotificationConstants.INDEX_SERVICE_RESOURCE_TYPE +
		".*";

	public static final String SUBSCR_ID_UNSPECIFIED = "UNSPECIFIED";

	/**
	 * Index last update date format pattern.
	 */
	private String statusDateFormatPattern;

	/**
	 * Index last update time zone.
	 */
	private String statusTimeZone;

	/**
	 * Subscription identified, setup when initializing IndexNotificationHandler
	 */
	@SuppressWarnings("unused")
	private String subscrId = SUBSCR_ID_UNSPECIFIED;

	/**
	 * ISSN service.
	 */
	private ISSNService snService;

	/**
	 * IS-PR service.
	 */
	private ISRegistryService registryService;

	/**
	 * IS-LU service.
	 */
	private ISLookUpService lookUpService;

	/**
	 * Notification consumer reference.
	 */
	private String consumerReference;

	/**
	 * Index Service IS profile identifier.
	 */
	private String indexServiceProfId;

	/**
	 * Internal search module.
	 */
	SearchModuleFacade internalSearchModule;

	/**
	 * index id <-> configuration relatioins container.
	 */
	private IMDFormatRelationsContainer mdFormatRelationsContainer;

	/**
	 * ResultSetIterator queue size.
	 */
	private int rsQueueSize;

	/**
	 * ResultSetIterator single call result size.
	 */
	private int rsPackageSize;

	/**
	 * Index record xslt location.
	 */
	private String indexRecordXSLTLocation;

	/**
	 * Maximum timeout (in seconds) for retrieving single result from ResultSet while feeding index.
	 */
	long maxRSIteratorTimeout;

	/**
	 * GlobalIndexContext object.
	 */
	private GlobalIndexContext globalIndexContext;

	/**
	 * Supported index status topic value. Built at service initialization.
	 */
	private String supportedIndexStatusTopic;

	/**
	 * ResultSet builder module. Provides direct access to the embedded IndexResultSet instance
	 * or builds ResultSetPortType to the remote ResultSet service.
	 */
	private IResultSetBuilder resultSetBuilder;
	
	/** The rs closing timeout. */
	private int rsClosingTimeout;

	
	/**
	 * Content parser builder module.
	 */
	private IDriverContentParserBuilder<FeedObject> contentParserBuilder;
	
	/**
	 * Spring init method.
	 */
	@Deprecated
	public void init() {
		/*
		supportedIndexStatusTopic = NotificationConstants.PREFIX_TOPIC_UPDATE_INDEX_SERVICE +
			indexServiceProfId +
			NotificationConstants.SUFFIX_TOPIC_UPDATE_INDEX_SERVICE_STATUS;

		String topic = NotificationConstants.PREFIX_SUBSCR_TOPIC_UPDATE_INDEX_SERVICE +
			indexServiceProfId +
			NotificationConstants.SUFFIX_SUBSCR_TOPIC_UPDATE_INDEX_SERVICE_STATUS;
		try {
			String serviceAddress = consumerReference.replace("?WSDL", "");
			W3CEndpointReference W3CEPR = EprUtils.buildW3CEPR(serviceAddress,consumerReference);
			String subscrIdIdxStatusUpd = snService.subscribe(W3CEPR, topic,
					NotificationConstants.TERMINATION_TIME_INFINITE);
			log.debug("subscribed to " + topic +
					" with subscrId:" + subscrIdIdxStatusUpd);
		} catch (ParserConfigurationException e){
			log.error("Problem during creating W3CEPR ", e);
		} catch(Exception e) {
			//exc may be thrown when using old IndexService schema lacking READ_ONLY status element
			log.error("couldn't subscribe with topic " + topic, e);
		}
		*/
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.nh.deleg.INotificationDelegatorClient#getTopicRegexPattern()
	 */
	public String getTopicRegexPattern() {
		return TOPIC_REGEX_PATTERN;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.nh.INotificationHandler#notify(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public boolean notify(String subscrId, String topic, String isId,
			String message) {

		if(topic.contains("BLACKBOARD"))
			this.indexServiceProfId = isId;

//		checking subscrId disabled
		/*
		if (!this.subscrId.equals(subscrId)) {
			log.warn("Invalid subscrId: "+subscrId+", expected: "+this.subscrId);
			return false;
		}
		*/

		if (supportedIndexStatusTopic!=null &&
				topic.equals(supportedIndexStatusTopic)) {
			return handleIndexServiceStatusUpdate(subscrId, topic, isId, message);
		}
		Profile profile = IndexProfileUnMarshaller.unmarshallIndexServiceResource(message);
		if (profile==null || profile.getHeader()==null ||
				profile.getBody()==null || !(profile.getBody() instanceof BlackboardBody)) {
			log.warn("Invalid message content: "+message);
			return false;
		}
		if (!ProfileHeader.INDEX_SERVICE_TYPE.equals(profile.getHeader().getResourceType())) {
			log.warn("Received blacboard message for unsupported service type: "+profile.getHeader().getResourceType());
			return false;
		}
		BlackboardBody bbBody = (BlackboardBody) profile.getBody();
		if (bbBody.getBlackboard()==null) {
			log.warn("No blackboard found in body: "+message);
			return false;
		}
		IBlackboardMessage lastMessage = bbBody.getBlackboard().getMessageForLastRequest();
		if (lastMessage==null) {
			if (bbBody.getBlackboard().getLastRequest()!=null)
				log.warn("Couldn't find last message for the last request id: "+
						bbBody.getBlackboard().getLastRequest().getValue());
			else
				log.warn("Couldn't find last message for null last request");
			return false;
		}
		if (!(lastMessage.getActionStatus() == Message.ActionStatus.ASSIGNED)) {
			log.warn("Supported action status = ASSIGNED, got: "+lastMessage.getActionStatus());
			return false;
		}

		return prepareAndSendEvent(lastMessage);
	}

	/**
	 * Handles index service status update notification.
	 * @param subscrId
	 * @param topic
	 * @param isId
	 * @param message
	 * @return true if notification successfully handled.
	 */
	boolean handleIndexServiceStatusUpdate(String subscrId, String topic, String isId,
			String message) {
		log.debug("handling index service status update notification...");
		Profile profile = IndexProfileUnMarshaller.unmarshallIndexServiceResource(message);
		if (profile==null || profile.getHeader()==null ||
				profile.getBody()==null || !(profile.getBody() instanceof IndexServiceBody)) {
			log.warn("Invalid message content: "+message +
					"\n Global service status will not be changed");
			return false;
		} else {
			if (((IndexServiceBody)profile.getBody()).getIndexServiceStatus()!=null &&
					((IndexServiceBody)profile.getBody()).getIndexServiceStatus().getReadOnlyStatus()!=null) {
				Boolean status = ((IndexServiceBody)profile.getBody()).getIndexServiceStatus().getReadOnlyStatus();
				log.debug("setting new index service read-only status: "+status);
				globalIndexContext.setReadOnlyStatus(status);
			} else {
				log.warn("No service status found in message content: "+message +
				"\n Global service status will not be changed");
			}
			return true;
		}
	}

	/**
	 * Prepares event and sends it to all registered listeners.
	 * Before sending event sends ONGING bb message, after sending event: DONE or FAILED.
	 * @param lastMessage
	 * @return true if all events sent succesfully.
	 */
	boolean prepareAndSendEvent(IBlackboardMessage lastMessage) {
		if (lastMessage.getAction()==null) {
			log.error("No action specified inside message!");
			return false;
		}
		switch (lastMessage.getAction()) {
		case CREATE: {
			return prepareAndSendCreateEvent(lastMessage);
		}
		case FEED: {
			return prepareAndSendFeedEvent(lastMessage);
		}
		case DELETE: {
			return prepareAndSendDeleteEvent(lastMessage);
		}
		default: {
			log.warn("Unsupported event action: "+lastMessage.getAction());
			return false;
		}
		}
	}

	/**
	 * Prepares and sends create index event.
	 * @param message
	 * @return true if succesfully created and sent
	 */
	private boolean prepareAndSendCreateEvent(IBlackboardMessage message) {
//		sends ONGOING bb message
		message.setActionStatus(ActionStatus.ONGOING);
		if (!sendBlackboardMessage(message))
			return false;

//		prepare param values
		String formatName = message.getParameter(Parameter.PARAM_NAME_FORMAT);
		String layoutName = message.getParameter(Parameter.PARAM_NAME_LAYOUT);
		String interpretationName = message.getParameter(Parameter.PARAM_NAME_INTERPRETATION);

		if (formatName==null || layoutName==null) {
			String errorContent = "Naither formatName nor layoutName can be null!";
			log.error(errorContent);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}
		List<DriverFieldSpec> driverFieldSpecList = null;
		try {
			driverFieldSpecList = getFieldsSpecification(formatName, layoutName);
			if (driverFieldSpecList==null || driverFieldSpecList.size()==0) {
				String errorContent = "Got no field specifications for " +
				"formatName: " + formatName + " and layoutName: "+layoutName;
				log.error(errorContent);
				throw new IndexServiceException(errorContent);
			}
		} catch (IndexServiceException e1) {
			String errorContent = "Exception occured when getting fields specs for " +
			"formatName: " + formatName + " and layoutName: "+layoutName;
			log.error(errorContent, e1);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}

//		creates IS profile
		ProfileHeader header = new ProfileHeader("", ProfileHeader.INDEX_DS_RESOURCE_TYPE,
				ProfileHeader.INDEX_DS_RESOURCE_KIND);
		header.setResourceURI(consumerReference);
		IndexBody body = new IndexBody();
		//config
		body.setMetaDataFormat(formatName);
		body.setMetaDataFormatInterpretation(interpretationName);
		body.setMetaDataFormatLayout(layoutName);
		body.setSize(0);

		//status
		DateUtils date = new DateUtils();
		body.setLastUpdate(date.getDateAsISO8601String());
		String profileContent = IndexProfileMarshaller.generateIndexProfile(header, body);
		String ixId;
		try {
			log.debug("registering indexProfile in IS: "+profileContent);
			ixId = registryService.registerProfile(profileContent);
		} catch (Exception e) {
			log.error("Exception occured when registering index profile: "
					+ profileContent, e);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					"Exception occured when registering index profile to IS"));
			sendBlackboardMessage(message);
			return false;
		}
//		creates event
		CreateIndexEvent event = new CreateIndexEvent(ixId);
		event.setFormat(formatName);
		event.setLayout(layoutName);
		event.setFieldSpecs(driverFieldSpecList);
		CreateIndexParamDTO params = new CreateIndexParamDTO();
		params.setIndexServiceProfId(indexServiceProfId);
		params.setMessage(message);
		params.setRegistryService(registryService);
		params.setFormat(formatName);
		params.setLayout(layoutName);
		params.setDriverFieldSpecList(driverFieldSpecList);
		params.setMdFormatRelationsContainer(mdFormatRelationsContainer);
		event.setIndexClient(new CreateIndexClient(params));
		@SuppressWarnings("unused")
		GenericEventResponse response = sendEvent(event);
//		sending DONE/FAILED bb message will be performed by callback object
		return true;
	}

	/**
	 * Returns fields specification for given format and layout.
	 * @param formatName
	 * @param layoutName
	 * @return fields specification
	 * @throws IndexServiceException
	 */
	public List<DriverFieldSpec> getFieldsSpecification(String formatName,
			String layoutName) throws IndexServiceException {
		if (formatName==null || layoutName==null)
			throw new InvalidParameterException("Neither formatName nor layoutName can be null!");

		try {
			String profileContent = retrieveMDFormatProfile(formatName);
			if (profileContent==null || profileContent.length()==0)
				throw new IndexServiceException("Couldn't find valid MDFormat profile content " +
						"for formatType: "+formatName);
			SAXParser saxParser = new SAXParser();
			MDFormatHandler handler = new MDFormatHandler();
			saxParser.setContentHandler(handler);
			saxParser.parse(new InputSource(new StringReader(profileContent)));
			if (handler.getDriverFieldSpecsMap()!=null) {
				return handler.getDriverFieldSpecsMap().get(layoutName);
			} else {
				log.error("No MDFormat data could be retrieved for " +
						"formatName: "+formatName);
				return null;
			}
		} catch (SAXException e) {
			throw new IndexServiceException("Exception occured when parsing " +
					"MDFormat profile!",e);
		} catch (IOException e) {
			throw new IndexServiceException("Exception occured when parsing " +
					"MDFormat profile!",e);
		} catch (ISLookUpDocumentNotFoundException e1) {
			String errorContent = "Exception occured when geting" +
					" mdformat profile from IS. Profile identified by" +
					formatName + " not found in IS Registry.";
			log.error(errorContent);
			throw new IndexServiceException(errorContent);
		} catch (ISLookUpException e) {
			throw new IndexServiceException("Exception occured when retrieving " +
					"MDFormat profile!",e);
		}
	}

	/**
	 * Retrieves MDFormat profile for given formatName.
	 * @param formatName
	 * @return MDFormat profile for given formatName
	 * @throws org.driver.islookup.GenericFaultMessage
	 * @throws ISLookUpException
	 */
	String retrieveMDFormatProfile(String formatName)
		throws ISLookUpException {
		return lookUpService.getResourceProfileByQuery(
				QueryProvider.findMDFormatResource(formatName));
	}

	String[] appendIdentifierField(String[] fields, String idField) {
		if (idField==null)
			return fields;
		if (fields==null || fields.length==0) {
			return new String[] {idField};
		} else {
			for (int i = 0; i < fields.length; i++) {
				if (idField.equals(fields[i]))
					return fields;
			}
			String[] result = new String[fields.length+1];
			for (int i = 0; i < fields.length; i++) {
				result[i] = fields[i];
			}
			result[fields.length] = idField;
			return result;
		}
	}

	/**
	 * Prepares and sends feed index event.
	 * @param message
	 * @return true if succesfully created and sent
	 */
	private boolean prepareAndSendFeedEvent(IBlackboardMessage message) {
//		sends ONGOING bb message
		message.setActionStatus(ActionStatus.ONGOING);
		if (!sendBlackboardMessage(message))
			return false;

//		retrieving IS index data
		String ixId = message.getParameter(Parameter.PARAM_NAME_ID);
		if (ixId==null) {
			String errorContent = "No index id among message parameters!";
			log.error(errorContent);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}
		String profileContent = null;
		try {
			profileContent = lookUpService.getResourceProfile(ixId);

		} catch (ISLookUpDocumentNotFoundException e1) {
			String errorContent = "Exception occured when geting" +
					" index profile from IS. Profile identified by" +
					ixId + " not found in IS Registry.";
			log.error(errorContent);
			return false;
		} catch (ISLookUpException e) {
			String errorContent = "Exception occured when getting index profile: "+e.getMessage();
			log.error(errorContent);
			return false;
		}
		if (profileContent==null) {
			String errorContent = "Couldn't find index profile in IS for ixId: "+ixId;
			log.error(errorContent);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}
		Profile indexProfile = IndexProfileUnMarshaller.unmarshallIndexResource(profileContent);
		if (indexProfile==null || indexProfile.getHeader()==null
				|| indexProfile.getBody()==null) {
			String errorContent = "Invalid indexProfile unmarshalled from: "+profileContent;
			log.error(errorContent);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}

//		checking if feeding stale indexDS profile, if so recreating index data.
		IndexBody indexBody = (IndexBody) indexProfile.getBody();
		if (indexBody.isInvalid() || message.getParameter
				(Parameter.PARAM_NAME_FEEDING_TYPE).equals("REFRESH")) {
			log.debug("got FEED notification of deprecated index, recreating...");
//			deleting deprecated index
			DeleteIndexEvent delEvent = new DeleteIndexEvent();
			delEvent.setIndexId(ixId);
			DeleteIndexParamDTO delParams = new DeleteIndexParamDTO();
			delParams.setIndexServiceProfId(null);
			delParams.setMessage(null);
			delParams.setRegistryService(null);
			delParams.setMdFormatRelationsContainer(mdFormatRelationsContainer);
			delParams.setPartOfTransaction(true);
			delEvent.setIndexClient(new DeleteIndexClient(delParams));
			GenericEventResponse delResponse = sendEvent(delEvent);
			if (delResponse.getStatus()!=Status.FINISHED) {
				String errorContent = "Invalid status after index deletion " +
						"for indexId: "+ixId;
				log.error(errorContent);
				message.setActionStatus(ActionStatus.FAILED);
				message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
						errorContent));
				sendBlackboardMessage(message);
				return false;
			}
//			reloading current field specification
			List<DriverFieldSpec> newFieldSpecList = null;
			String formatName = indexBody.getMetaDataFormat();
			String layoutName = indexBody.getMetaDataFormatLayout();
			try {
				newFieldSpecList = getFieldsSpecification(formatName, layoutName);
				if (newFieldSpecList==null || newFieldSpecList.size()==0) {
					String errorContent = "Got no field specifications for " +
					"formatName: " + formatName + " and layoutName: "+layoutName;
					log.error(errorContent);
					throw new IndexServiceException(errorContent);
				}
			} catch (IndexServiceException e1) {
				String errorContent = "Exception occured when getting fields specs for " +
				"formatName: " + formatName + " and layoutName: "+layoutName;
				log.error(errorContent, e1);
				message.setActionStatus(ActionStatus.FAILED);
				message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
						errorContent));
				sendBlackboardMessage(message);
				return false;
			}
//			creating index with updated configuration
			CreateIndexEvent createEvent = new CreateIndexEvent(ixId);
			createEvent.setFormat(formatName);
			createEvent.setLayout(layoutName);
			createEvent.setFieldSpecs(newFieldSpecList);
			CreateIndexParamDTO createParams = new CreateIndexParamDTO();
			createParams.setIndexServiceProfId(null);
			createParams.setMessage(null);
			createParams.setRegistryService(null);
			createParams.setFormat(formatName);
			createParams.setLayout(layoutName);
			createParams.setDriverFieldSpecList(newFieldSpecList);
			createParams.setMdFormatRelationsContainer(mdFormatRelationsContainer);
			createParams.setPartOfTransaction(true);
			createEvent.setIndexClient(new CreateIndexClient(createParams));
			GenericEventResponse createResponse = sendEvent(createEvent);
			if (createResponse.getStatus()!=Status.FINISHED) {
				String errorContent = "Invalid status after index creation " +
						"for indexId: "+ixId;
				log.error(errorContent);
				message.setActionStatus(ActionStatus.FAILED);
				message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
						errorContent));
				sendBlackboardMessage(message);
				return false;
			}
		}//end of index recreation

//		preparing feeding event
		FeedIndexEvent event = new FeedIndexEvent();
		event.setIndexId(message.getParameter(Parameter.PARAM_NAME_ID));
		event.setType(message.getParameter(Parameter.PARAM_NAME_FEEDING_TYPE));

		String resultSetEPRBase64Enc = message.getParameter(Parameter.PARAM_NAME_RESULTSET_EPR);
		
		ContentParserContext cpCtx = new ContentParserContext();
		cpCtx.setProperty(ContentParserContext.KEY_XSLT_LOCATION, indexRecordXSLTLocation);
		event.setFeedIterator(new ResultSetFeedIterator
				(decode(resultSetEPRBase64Enc), resultSetBuilder,
				contentParserBuilder.buildParser(cpCtx), 
				rsQueueSize, rsPackageSize, 
				maxRSIteratorTimeout, this.rsClosingTimeout));
		FeedIndexParamDTO params = new FeedIndexParamDTO();
		params.setIndexProfile(indexProfile);
		params.setIndexServiceProfId(indexServiceProfId);
		params.setMessage(message);
		params.setRegistryService(registryService);
		//params.setStatusDateFormatPattern(statusDateFormatPattern);
		//params.setStatusTimeZone(statusTimeZone);
		event.setIndexClient(new FeedIndexClient(params));
		@SuppressWarnings("unused")
		GenericEventResponse response = sendEvent(event);
//		sending DONE/FAILED bb message will be performed by callback object
		return true;
	}

	/**
	 * Decodes resultSetEPRBase64Enc.
	 * Returns resultSetEPR decoded from base64 format.
	 * @param resultSetEPRBase64Enc base64 format of resultSetEPR
	 * @return resultSetEPR
	 */
	static String decode(String resultSetEPRBase64Enc) {
		if (resultSetEPRBase64Enc==null)
			return null;
		BASE64Decoder decoder = new BASE64Decoder();
		try {
//			FIXME this is hotfix to spaces->breaklines normalisation
//			after decoding stopped working
//			if possible different solution should be taken
			resultSetEPRBase64Enc = resultSetEPRBase64Enc.replace(' ','\n');
			return new String(decoder.decodeBuffer(resultSetEPRBase64Enc));
		} catch (IOException e) {
			log.error("Unexpected exception occured when parsing resultSetEPRBase64Enc: "
					+ resultSetEPRBase64Enc, e);
			return null;
		}
	}

	/**
	 * Prepares and sends feed index event.
	 * @param message
	 * @return true if succesfully created and sent
	 */
	private boolean prepareAndSendDeleteEvent(IBlackboardMessage message) {
//		sends ONGOING bb message
		message.setActionStatus(ActionStatus.ONGOING);
		if (!sendBlackboardMessage(message))
			return false;

		String ixId = message.getParameter(Parameter.PARAM_NAME_ID);
		if (ixId==null) {
			String errorContent = "No index id among message parameters!";
			log.error(errorContent);
			message.setActionStatus(ActionStatus.FAILED);
			message.overwriteParameter(new Parameter(Parameter.PARAM_NAME_ERROR,
					errorContent));
			sendBlackboardMessage(message);
			return false;
		}

		DeleteIndexEvent event = new DeleteIndexEvent();
		event.setIndexId(message.getParameter(Parameter.PARAM_NAME_ID));
		DeleteIndexParamDTO params = new DeleteIndexParamDTO();
		params.setIndexServiceProfId(indexServiceProfId);
		params.setMessage(message);
		params.setRegistryService(registryService);
		params.setMdFormatRelationsContainer(mdFormatRelationsContainer);
		event.setIndexClient(new DeleteIndexClient(params));
		@SuppressWarnings("unused")
		GenericEventResponse response = sendEvent(event);
//		sending DONE/FAILED bb message will be performed by callback object
		return true;
	}

	/**
	 * Sends blackboard message to IS.
	 * @param message
	 * @return true if succesfully sent
	 */
	private boolean sendBlackboardMessage(IBlackboardMessage message) {
		try {
			log.debug("sending bb message to IS: \n" +
					ProfileMarshaller.generateMessagePartOfBlackBoard(message));
			registryService.replyBlackBoardMessage(indexServiceProfId,
					ProfileMarshaller.generateMessagePartOfBlackBoard(message));
			return true;
//		} catch (GenericFaultMessage e) {
		} catch (Exception e) {
			log.error("Exception occured when sending blackboard message: "+
					ProfileMarshaller.generateMessagePartOfBlackBoard(message),e);
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.index.ws.commons.event.IIndexEventProducer#sendEvent(eu.dnetlib.data.index.ws.commons.event.IGenericEvent)
	 */
	public GenericEventResponse sendEvent(IGenericEvent event) {
		if (event==null)
			return null;
		GenericEventResponse response = internalSearchModule.notify(event);
		if (response==null)
				log.warn("null response returned by listener " + internalSearchModule);
		return response;
	}

	/**
	 * Returns ISSN service.
	 * @return ISSN service
	 */
	public ISSNService getSnService() {
		return snService;
	}

	/**
	 * Sets ISSN service.
	 * @param snService
	 */
	public void setSnService(ISSNService snService) {
		this.snService = snService;
	}

	/**
	 * Returns IS-PR service.
	 * @return IS-PR service
	 */
	public ISRegistryService getRegistryService() {
		return registryService;
	}

	/**
	 * Sets IS-PR service.
	 * @param registryService
	 */
	public void setRegistryService(ISRegistryService registryService) {
		this.registryService = registryService;
	}

	/**
	 * Returns IS-LU service.
	 * @return IS-LU service
	 */
	public ISLookUpService getLookUpService() {
		return lookUpService;
	}

	/**
	 * Sets IS-LU service.
	 * @param lookUpService
	 */
	public void setLookUpService(ISLookUpService lookUpService) {
		this.lookUpService = lookUpService;
	}

	/**
	 * Returns consumer reference for ISSN subscription.
	 * @return consumer reference for ISSN subscription
	 */
	public String getConsumerReference() {
		return consumerReference;
	}

	/**
	 * Sets consumer reference for ISSN subscription.
	 * @param consumerReference
	 */
	public void setConsumerReference(String consumerReference) {
		this.consumerReference = consumerReference;
	}

	/**
	 * Returns index service profile identifier.
	 * @return index service profile identifier
	 */
	public String getIndexServiceProfId() {
		return indexServiceProfId;
	}

	/**
	 * Sets index service profile identifier.
	 * @param indexServiceProfId
	 */
	public void setIndexServiceProfId(String indexServiceProfId) {
		this.indexServiceProfId = indexServiceProfId;
	}

	/**
	 * Returns status DateFormat pattern.
	 * @return status DateFormat pattern
	 */
	public String getStatusDateFormatPattern() {
		return statusDateFormatPattern;
	}

	/**
	 * Sets status DateFormat pattern.
	 * @param statusDateFormatPattern
	 */
	public void setStatusDateFormatPattern(String statusDateFormatPattern) {
		this.statusDateFormatPattern = statusDateFormatPattern;
	}

	/**
	 * Returns status time zone.
	 * @return status time zone
	 */
	public String getStatusTimeZone() {
		return statusTimeZone;
	}

	/**
	 * Sets status time zone.
	 * @param statusTimeZone
	 */
	public void setStatusTimeZone(String statusTimeZone) {
		this.statusTimeZone = statusTimeZone;
	}

	/**
	 * Returns ResultSetIterator single call result size.
	 * @return ResultSetIterator single call result size
	 */
	public int getRsPackageSize() {
		return rsPackageSize;
	}

	/**
	 * Sets ResultSetIterator single call result size.
	 * @param rsPackageSize
	 */
	public void setRsPackageSize(int rsPackageSize) {
		this.rsPackageSize = rsPackageSize;
	}

	/**
	 * Returns ResultSetIterator queue size.
	 * @return ResultSetIterator queue size
	 */
	public int getRsQueueSize() {
		return rsQueueSize;
	}

	/**
	 * Sets ResultSetIterator queue size.
	 * @param rsQueueSize
	 */
	public void setRsQueueSize(int rsQueueSize) {
		this.rsQueueSize = rsQueueSize;
	}

	/**
	 * Returns internal search module.
	 * @return internal search module
	 */
	public SearchModuleFacade getInternalSearchModule() {
		return internalSearchModule;
	}

	/**
	 * Sets internal search module.
	 * @param internalSearchModule
	 */
	public void setInternalSearchModule(SearchModuleFacade internalSearchModule) {
		this.internalSearchModule = internalSearchModule;
	}

	/**
	 * Returns maximum timeout (in seconds) for retrieving single result
	 * from ResultSet while feeding index.
	 * @return maximum timeout (in seconds) for retrieving single result
	 * from ResultSet while feeding index
	 */
	public long getMaxRSIteratorTimeout() {
		return maxRSIteratorTimeout;
	}

	/**
	 * Sets maximum timeout (in seconds) for retrieving single result
	 * from ResultSet while feeding index.
	 * @param maxRSIteratorTimeout
	 */
	public void setMaxRSIteratorTimeout(long maxRSIteratorTimeout) {
		this.maxRSIteratorTimeout = maxRSIteratorTimeout;
	}

	/**
	 * Returns index record xslt location.
	 * @return index record xslt location
	 */
	public String getIndexRecordXSLTLocation() {
		return indexRecordXSLTLocation;
	}

	/**
	 * Sets index record xslt location.
	 * @param indexRecordXSLTLocation
	 */
	public void setIndexRecordXSLTLocation(String indexRecordXSLTLocation) {
		this.indexRecordXSLTLocation = indexRecordXSLTLocation;
	}

	/**
	 * Returns index id <-> configuration relatioins container.
	 * @return index id <-> configuration relatioins container
	 */
	public IMDFormatRelationsContainer getMdFormatRelationsContainer() {
		return mdFormatRelationsContainer;
	}

	/**
	 * Sets index id <-> configuration relatioins container.
	 * @param mdFormatRelationsContainer
	 */
	public void setMdFormatRelationsContainer(
			IMDFormatRelationsContainer mdFormatRelationsContainer) {
		this.mdFormatRelationsContainer = mdFormatRelationsContainer;
	}

	/**
	 * Returns GlobalIndexContext object.
	 * @return GlobalIndexContext object
	 */
	public GlobalIndexContext getGlobalIndexContext() {
		return globalIndexContext;
	}

	/**
	 * Sets GlobalIndexContext object.
	 * @param globalIndexContext
	 */
	public void setGlobalIndexContext(GlobalIndexContext globalIndexContext) {
		this.globalIndexContext = globalIndexContext;
	}

	/**
	 * Returns ResultSet builder module.
	 * @return ResultSet builder module
	 */
	public IResultSetBuilder getResultSetBuilder() {
		return resultSetBuilder;
	}

	/**
	 * Sets ResultSet builder module.
	 * @param resultSetBuilder
	 */
	public void setResultSetBuilder(IResultSetBuilder resultSetBuilder) {
		this.resultSetBuilder = resultSetBuilder;
	}
	
	/**
	 * Gets the rs closing timeout.
	 * 
	 * @return the rs closing timeout
	 */
	public int getRsClosingTimeout() {
		return this.rsClosingTimeout;
	}

	/**
	 * Sets the rs closing timeout.
	 * 
	 * @param rsClosingTimeout the new rs closing timeout
	 */
	public void setRsClosingTimeout(int rsClosingTimeout) {
		this.rsClosingTimeout = rsClosingTimeout;
	}

	/**
	 * Sets content parser builder module.
	 * @param contentParserBuilder
	 */
	public void setContentParserBuilder(
			IDriverContentParserBuilder<FeedObject> contentParserBuilder) {
		this.contentParserBuilder = contentParserBuilder;
	}

}
