/** * Demonstrates how to use the cursor to be able to pass over an complete set of data * * @param pm Persistence manager instance to use - let open at the end to allow possible object * updates later * @param cursorString Representation of a cursor, to be used to get the next set of data to * process * @param range Number of data to process at this particular stage * @param defaultSource Value to apply to any Demand instance without a default value * @return New representation of the cursor, ready for a next process call */ @SuppressWarnings("unchecked") public static String updateSource( PersistenceManager pm, String cursorString, int range, Source defaultSource) { Query query = null; try { query = pm.newQuery(Demand.class); if (cursorString != null) { Map<String, Object> extensionMap = new HashMap<String, Object>(); extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, Cursor.fromWebSafeString(cursorString)); query.setExtensions(extensionMap); } query.setRange(0, range); List<Demand> results = (List<Demand>) query.execute(); if (results.iterator().hasNext()) { for (Demand demand : results) { // Initialize the new field if necessary. By checking first that the field is null, we // allow this migration to be safely run multiple times. if (demand.getSource() == null) { demand.setSource(defaultSource); pm.makePersistent(demand); } } cursorString = JDOCursorHelper.getCursor(results).toWebSafeString(); } else { // no results cursorString = null; } } finally { query.closeAll(); } return cursorString; }
/** * Utility method update a proposal with the given parameters and triggering the associated * workflow steps * * @param pm Persistence manager instance to use - let open at the end to allow possible object * updates later * @param rawCommand Reference of the command which initiated the process, is <code>null</code> if * initiated by a REST API call * @param proposalKey Resource identifier * @param parameters Parameters produced by the Command line parser or transmitted via the REST * API * @param ownerKey Key of the sale associate who owns the proposal to be updated * @param saConsumerRecordKey Key of the consumer record attached to the sale associate * @param isUserAdmin * @return Just updated proposal * @throws DataSourceException if the retrieval of the last created proposal or of the location * information fail * @throws InvalidIdentifierException if there's an issue with the Proposal identifier is invalid * @throws InvalidStateException if the Proposal is not update-able * @throws CommunicationException if the communication of the update confirmation fails */ public static Proposal updateProposal( PersistenceManager pm, RawCommand rawCommand, Long proposalKey, JsonObject parameters, Long ownerKey, Long saConsumerRecordKey, boolean isUserAdmin) throws DataSourceException, InvalidIdentifierException, InvalidStateException, CommunicationException { SaleAssociate owner = getSaleAssociateOperations().getSaleAssociate(pm, ownerKey); Consumer saConsumerRecord = getConsumerOperations().getConsumer(pm, saConsumerRecordKey); Proposal proposal = getProposalOperations() .getProposal( pm, proposalKey, isUserAdmin ? null : ownerKey, isUserAdmin ? null : owner.getStoreKey()); State currentState = proposal.getState(); // Workflow state change if (parameters.size() == 1 && parameters.containsKey(Command.STATE)) { String proposedState = parameters.getString(Command.STATE); // Close if (State.confirmed.equals(currentState) && State.closed.toString().equals(proposedState)) { // Update the user's statistics owner.setClosedProposalNb( owner.getClosedProposalNb() == null ? 1 : owner.getClosedProposalNb() + 1); owner = BaseSteps.getSaleAssociateOperations().updateSaleAssociate(pm, owner); // Update the store's statistics Store store = getStoreOperations().getStore(pm, proposal.getStoreKey()); store.setClosedProposalNb( store.getClosedProposalNb() == null ? 1 : store.getClosedProposalNb() + 1); store = BaseSteps.getStoreOperations().updateStore(pm, store); // Get the associated demand Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); if (rawCommand != null && !Source.robot.equals(rawCommand.getSource())) { Locale locale = saConsumerRecord.getLocale(); MessageGenerator msgGen = new MessageGenerator(rawCommand.getSource(), demand.getHashTags(), locale); msgGen .put("proposal>owner>name", saConsumerRecord.getName()) .fetch(demand) .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel())) { subject = rawCommand.getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); communicateToConsumer( msgGen.getCommunicationChannel(), subject, saConsumerRecord, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CLOSING_OK_TO_ASSOCIATE)}); } if (!State.closed.equals(demand.getState())) { // Get demand owner Consumer demandOwner = getConsumerOperations().getConsumer(pm, demand.getOwnerKey()); Location location = getLocationOperations().getLocation(pm, store.getLocationKey()); Registrar registrar = getRegistrarOperations().getRegistrar(pm, store.getRegistrarKey()); // Inform Proposal owner about the closing Locale locale = demandOwner.getLocale(); MessageGenerator msgGen = new MessageGenerator( demandOwner.getPreferredConnection(), demand.getHashTags(), locale); msgGen .put("demand>owner>name", demandOwner.getName()) .fetch(demand) .fetch(proposal) .fetch(store) .fetch(location, "store") .fetch(registrar) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)) .put( "command>footer", LabelExtractor.get(ResourceFileId.fourth, "command_message_footer", locale)); String closeDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_close", msgGen.getParameters(), locale); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel()) && Source.mail.equals(demand.getSource())) { subject = BaseSteps.getRawCommandOperations() .getRawCommand(pm, demand.getRawCommandId()) .getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); msgGen .put( "command>threadSubject", BaseConnector.prepareMailToSubject( MailConnector.prepareSubjectAsResponse(subject, locale))) .put("command>closeDemand", BaseConnector.prepareMailToBody(closeDemand)); try { communicateToConsumer( msgGen.getCommunicationChannel(), subject, demandOwner, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CLOSING_OK_TO_CONSUMER)}); } catch (CommunicationException e) { // Not a critical error, should not block the rest of the process getLogger().warning("Cannot inform " + demand.getOwnerKey()); } } // No need to bother CC-ed } // Cancel else if (!State.closed.equals(currentState) && State.cancelled.toString().equals(proposedState)) { proposal.setCancelerKey(saConsumerRecord.getKey()); // Get the associated demand Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); demand.removeProposalKey(proposalKey); // Confirm the proposal canceling to the owner if (rawCommand != null) { Locale locale = saConsumerRecord.getLocale(); MessageGenerator msgGen = new MessageGenerator(rawCommand.getSource(), demand.getHashTags(), locale); msgGen .put("proposal>owner>name", saConsumerRecord.getName()) .fetch(demand) .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel())) { subject = rawCommand.getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); communicateToConsumer( msgGen.getCommunicationChannel(), subject, saConsumerRecord, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CANCELLATION_OK_TO_ASSOCIATE)}); } if (State.confirmed.equals(currentState)) { Consumer demandOwner = getConsumerOperations().getConsumer(pm, demand.getOwnerKey()); Location location = getLocationOperations().getLocation(pm, demand.getLocationKey()); // FIXME: Place the associated demand in the published state again if not expired demand.setState(State.published); demand.updateModificationDate(); // To be sure it's picked-up by the next cron job // 'processPublishedDemands' // Notify Consumer about the confirmed Proposal cancellation Locale locale = demandOwner.getLocale(); MessageGenerator msgGen = new MessageGenerator( demandOwner.getPreferredConnection(), demand.getHashTags(), locale); msgGen .put("demand>owner>name", demandOwner.getName()) .fetch(demand) .fetch(location, "demand") .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)) .put( "command>footer", LabelExtractor.get(ResourceFileId.fourth, "command_message_footer", locale)); String cancelDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_cancel", msgGen.getParameters(), locale); String updateDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_update", msgGen.getParameters(), locale); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel()) && Source.mail.equals(demand.getSource())) { subject = getRawCommandOperations().getRawCommand(pm, demand.getRawCommandId()).getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); msgGen .put("command>threadSubject", BaseConnector.prepareMailToSubject(subject)) .put("command>cancelDemand", BaseConnector.prepareMailToBody(cancelDemand)) .put("command>updateDemand", BaseConnector.prepareMailToBody(updateDemand)); try { communicateToConsumer( msgGen.getCommunicationChannel(), subject, demandOwner, new String[] { msgGen.getMessage(MessageId.PROPOSAL_CONFIRMED_CANCELLATION_OK_TO_CONSUMER) }); } catch (CommunicationException e) { // Not a critical error, should not block the rest of the process getLogger().warning("Cannot inform " + demand.getOwnerKey()); } } getDemandOperations().updateDemand(pm, demand); } else { throw new InvalidStateException( "Invalid state change attempt to: " + proposedState, currentState.toString(), proposedState); } proposal.setState(proposedState); proposal = getProposalOperations().updateProposal(pm, proposal); } // Normal attribute update else if (State.opened.equals(currentState) || State.published.equals(currentState) || State.invalid.equals(currentState)) { // Integrate updates proposal.fromJson(parameters, isUserAdmin, false); // Prepare as a new Demand proposal.setState(State.opened); // Will force the re-validation of the entire demand // Persist updates proposal = getProposalOperations().updateProposal(pm, proposal); // Detach the proposal from the associated demand (just in case it's now invalid) Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); demand.removeProposalKey(proposalKey); getDemandOperations().updateDemand(pm, demand); // Related workflow step MaelzelServlet.triggerValidationTask(proposal); } else { throw new InvalidStateException( "Entity not in modifiable state", currentState.toString(), null); } return proposal; }