This project has retired. For details please refer to its Attic page.
UDDICustodyTransferImpl.java

UDDICustodyTransferImpl.java

/*
 * Copyright 2001-2008 The Apache Software Foundation.
 * 
 * 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 org.apache.juddi.api.impl;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.juddi.api.util.CustodyTransferQuery;
import org.apache.juddi.api.util.QueryStatus;
import org.apache.juddi.config.AppConfig;
import org.apache.juddi.config.PersistenceManager;
import org.apache.juddi.config.Property;
import org.apache.juddi.mapping.MappingApiToModel;
import org.apache.juddi.mapping.MappingModelToApi;
import org.apache.juddi.model.BindingTemplate;
import org.apache.juddi.model.BusinessEntity;
import org.apache.juddi.model.BusinessService;
import org.apache.juddi.model.Operator;
import org.apache.juddi.model.Tmodel;
import org.apache.juddi.model.TransferTokenKey;
import org.apache.juddi.model.UddiEntity;
import org.apache.juddi.model.UddiEntityPublisher;
import org.apache.juddi.query.util.DynamicQuery;
import org.apache.juddi.replication.ReplicationNotifier;
import org.apache.juddi.v3.client.UDDIService;
import org.apache.juddi.v3.error.ErrorMessage;
import org.apache.juddi.v3.error.FatalErrorException;
import org.apache.juddi.v3.error.InvalidValueException;
import org.apache.juddi.v3.error.TransferNotAllowedException;
import org.apache.juddi.validation.ValidateCustodyTransfer;
import org.uddi.api_v3.OperationalInfo;
import org.uddi.custody_v3.DiscardTransferToken;
import org.uddi.custody_v3.KeyBag;
import org.uddi.custody_v3.TransferEntities;
import org.uddi.custody_v3.TransferOperationalInfo;
import org.uddi.repl_v3.ChangeRecord;
import org.uddi.repl_v3.ChangeRecordIDType;
import org.uddi.repl_v3.ChangeRecordNewData;
import org.uddi.repl_v3.TransferCustody;
import org.uddi.v3_service.DispositionReportFaultMessage;
import org.uddi.v3_service.UDDICustodyTransferPortType;
import org.uddi.v3_service.UDDIReplicationPortType;

/**
 * This implements the UDDI v3 Custody Transfer API web service
 *
 */
@WebService(serviceName = "UDDICustodyTransferService",
        endpointInterface = "org.uddi.v3_service.UDDICustodyTransferPortType",
        targetNamespace = "urn:uddi-org:v3_service")
public class UDDICustodyTransferImpl extends AuthenticatedService implements UDDICustodyTransferPortType {

        public static final String TRANSFER_TOKEN_PREFIX = "transfertoken:";
        public static final int DEFAULT_TRANSFEREXPIRATION_DAYS = 3;

        private static Log logger = LogFactory.getLog(UDDICustodyTransferImpl.class);

        private static DatatypeFactory df = null;
        private UDDIServiceCounter serviceCounter;

        public UDDICustodyTransferImpl() {
                super();
                serviceCounter = ServiceCounterLifecycleResource.getServiceCounter(this.getClass());
                init();
        }
        
        private static synchronized void init() {
                if (df == null) {
                        try {
                                df = DatatypeFactory.newInstance();
                        } catch (DatatypeConfigurationException ex) {
                                Logger.getLogger(UDDICustodyTransferImpl.class.getName()).log(Level.SEVERE, null, ex);
                        }
                }
        }

        @SuppressWarnings("unchecked")
        @Override
        public void discardTransferToken(DiscardTransferToken body)
                throws DispositionReportFaultMessage {
                long startTime = System.currentTimeMillis();

                EntityManager em = PersistenceManager.getEntityManager();
                EntityTransaction tx = em.getTransaction();
                try {
                        tx.begin();

                        UddiEntityPublisher publisher = this.getEntityPublisher(em, body.getAuthInfo());

                        new ValidateCustodyTransfer(publisher).validateDiscardTransferToken(em, body);

                        org.uddi.custody_v3.TransferToken apiTransferToken = body.getTransferToken();
                        if (apiTransferToken != null) {
                                String transferTokenId;
                                try {
                                    transferTokenId = new String(apiTransferToken.getOpaqueToken(), UTF8);
                                } catch (UnsupportedEncodingException ex) {
                                    throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
                                }
                                org.apache.juddi.model.TransferToken modelTransferToken = em.find(org.apache.juddi.model.TransferToken.class, transferTokenId);
                                if (modelTransferToken != null) {
                                        em.remove(modelTransferToken);
                                }
                        }

                        KeyBag keyBag = body.getKeyBag();
                        if (keyBag != null) {
                                List<String> keyList = keyBag.getKey();
                                Vector<DynamicQuery.Parameter> params = new Vector<DynamicQuery.Parameter>(0);
                                for (String key : keyList) {
                                        // Creating parameters for key-checking query
                                        DynamicQuery.Parameter param = new DynamicQuery.Parameter("UPPER(ttk.entityKey)",
                                                key.toUpperCase(),
                                                DynamicQuery.PREDICATE_EQUALS);

                                        params.add(param);
                                }

                                // Find the associated transfer tokens and remove them.
                                DynamicQuery getTokensQry = new DynamicQuery();
                                getTokensQry.append("select distinct ttk.transferToken from TransferTokenKey ttk").pad();
                                getTokensQry.WHERE().pad().appendGroupedOr(params.toArray(new DynamicQuery.Parameter[0]));

                                Query qry = getTokensQry.buildJPAQuery(em);
                                List<org.apache.juddi.model.TransferToken> tokensToDelete = qry.getResultList();
                                if (tokensToDelete != null && tokensToDelete.size() > 0) {
                                        for (org.apache.juddi.model.TransferToken tt : tokensToDelete) {
                                                em.remove(tt);
                                        }
                                }
                        }

                        tx.commit();
                        long procTime = System.currentTimeMillis() - startTime;
                        serviceCounter.update(CustodyTransferQuery.DISCARD_TRANSFERTOKEN,
                                QueryStatus.SUCCESS, procTime);

                } finally {
                        if (tx.isActive()) {
                                tx.rollback();
                        }
                        em.close();
                }
        }

        @Override
        public void getTransferToken(String authInfo, KeyBag keyBag,
                Holder<String> nodeID, Holder<XMLGregorianCalendar> expirationTime,
                Holder<byte[]> opaqueToken) throws DispositionReportFaultMessage {
                long startTime = System.currentTimeMillis();

                EntityManager em = PersistenceManager.getEntityManager();
                EntityTransaction tx = em.getTransaction();
                try {
                        tx.begin();

                        UddiEntityPublisher publisher = this.getEntityPublisher(em, authInfo);

                        new ValidateCustodyTransfer(publisher).validateGetTransferToken(em, keyBag);

                        int transferExpirationDays = DEFAULT_TRANSFEREXPIRATION_DAYS;
                        try {
                                transferExpirationDays = AppConfig.getConfiguration().getInt(Property.JUDDI_TRANSFER_EXPIRATION_DAYS);
                                // For output
                                nodeID.value = AppConfig.getConfiguration().getString(Property.JUDDI_NODE_ID);
                        } catch (ConfigurationException ce) {
                                throw new FatalErrorException(new ErrorMessage("errors.configuration.Retrieval"));
                        }

                        String transferKey = TRANSFER_TOKEN_PREFIX + UUID.randomUUID();
                        org.apache.juddi.model.TransferToken transferToken = new org.apache.juddi.model.TransferToken();
                        transferToken.setTransferToken(transferKey);
                        try {
                            // For output
                            opaqueToken.value = transferKey.getBytes(UTF8);
                        } catch (UnsupportedEncodingException ex) {
                            throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
                        }

                        GregorianCalendar gc = new GregorianCalendar();
                        gc.add(GregorianCalendar.DAY_OF_MONTH, transferExpirationDays);

                        transferToken.setExpirationDate(gc.getTime());

                        try {
                                DatatypeFactory df = DatatypeFactory.newInstance();
                                // For output
                                expirationTime.value = df.newXMLGregorianCalendar(gc);
                        } catch (DatatypeConfigurationException ce) {
                                throw new FatalErrorException(new ErrorMessage("errors.Unspecified"));
                        }

                        List<String> keyList = keyBag.getKey();
                        for (String key : keyList) {
                                TransferTokenKey tokenKey = new TransferTokenKey(transferToken, key);
                                transferToken.getTransferKeys().add(tokenKey);
                        }

                        em.persist(transferToken);

                        tx.commit();

                        long procTime = System.currentTimeMillis() - startTime;
                        serviceCounter.update(CustodyTransferQuery.GET_TRANSFERTOKEN,
                                QueryStatus.SUCCESS, procTime);

                } finally {
                        if (tx.isActive()) {
                                tx.rollback();
                        }
                        em.close();
                }
        }

        @Override
        public void transferEntities(TransferEntities body)
                throws DispositionReportFaultMessage {
                long startTime = System.currentTimeMillis();

                EntityManager em = PersistenceManager.getEntityManager();
                EntityTransaction tx = em.getTransaction();
                List<ChangeRecord> changes = new ArrayList<ChangeRecord>();
                try {
                        tx.begin();

                        UddiEntityPublisher publisher = this.getEntityPublisher(em, body.getAuthInfo());

                        ValidateCustodyTransfer verifier = new ValidateCustodyTransfer(publisher);

                        //if the destination transfer is to a different node, 
                        if (!verifier.validateTransferEntities(em, body)) {
                                //i don't own these entities, so tell the owner to transfer to me.

                                //look up the replication config endpoint for that node and trigger the transfer, then return
                                //ok this is a node to node transfer, first up a replication client to the destination node
                                String sourceNode = null;
                                try {
                                        KeyBag keyBag = body.getKeyBag();
                                        List<String> keyList = keyBag.getKey();
                                        for (String key : keyList) {
                                                UddiEntity uddiEntity = em.find(UddiEntity.class, key);
                                                
                                                if (uddiEntity!=null) {
                                                        uddiEntity.setIsTransferInProgress(true);
                                                        sourceNode = uddiEntity.getNodeId();
                                                        em.merge(uddiEntity);
                                                        //save the fact we are expecting a transfer
                                                }
                                                else
                                                {
                                                        logger.warn("couldn't find a record for key " + key);
                                                }
                                        }
                                        if (sourceNode==null){
                                                logger.warn("unable to process transfer, could not locate the source node, perhaps it hasn't been replicated to this node yet?")
                                                        ;
                                                throw new Exception("unable to process transfer, could not locate the source node for any of the specific keys, perhaps it hasn't been replicated to this node yet?");
                                        }

                                        UDDIReplicationPortType replicationClient = getReplicationClient(sourceNode);
                                        if (replicationClient == null) {
                                                throw new Exception("Unknown node. is it in the replication graph?" + sourceNode);
                                        }
                                        TransferCustody transferCustody = new TransferCustody();
                                        transferCustody.setTransferToken(body.getTransferToken());
                                        transferCustody.setKeyBag(body.getKeyBag());
                                        transferCustody.setTransferOperationalInfo(new TransferOperationalInfo());
                                        transferCustody.getTransferOperationalInfo().setAuthorizedName(publisher.getAuthorizedName());
                                        transferCustody.getTransferOperationalInfo().setNodeID(getNode());

                                        //and trigger the transfer
                                        logger.info("AUDIT, transfering " + transferCustody.getKeyBag().getKey().size() + " entities to " + publisher.getAuthorizedName() + " at node " + getNode() + " from source " + sourceNode);
                                        replicationClient.transferCustody(transferCustody);
                                } catch (DispositionReportFaultMessage df) {
                                        logger.error("Unable to transfer entities from " + sourceNode + " to node " + getNode() + " to user " + publisher.getAuthorizedName(), df);
                                        throw new TransferNotAllowedException(new ErrorMessage("E_transferBlocked", df.getMessage()));
                                } catch (Exception ex) {
                                        logger.error("Unable to transfer entities from " + sourceNode + " to node " + getNode() + " to user " + publisher.getAuthorizedName(), ex);
                                        throw new TransferNotAllowedException(new ErrorMessage("E_transferBlocked", ex.getMessage()));
                                }

                        } else {
                                changes.addAll(executeTransfer(body, em, publisher.getAuthorizedName(), getNode()));
                                //all of the items to be transfer are owned locally by *this node.

                        }
                        tx.commit();
                        //we need to do something for replication purposes here
                        //enqueue notifications and storage of the changed records
                        for (ChangeRecord c : changes) {
                                try {
                                        c.setChangeID(new ChangeRecordIDType());
                                        c.getChangeID().setNodeID(getNode());
                                        c.getChangeID().setOriginatingUSN(null);
                                        ReplicationNotifier.enqueue(MappingApiToModel.mapChangeRecord(c));
                                } catch (UnsupportedEncodingException ex) {
                                        logger.error("", ex);
                                }
                        }
                        long procTime = System.currentTimeMillis() - startTime;
                        serviceCounter.update(CustodyTransferQuery.TRANSFER_ENTITIES,
                                QueryStatus.SUCCESS, procTime);

                } finally {
                        if (tx.isActive()) {
                                tx.rollback();
                        }
                        em.close();
                }

        }

        private synchronized UDDIReplicationPortType getReplicationClient(String node) {

                UDDIService svc = new UDDIService();
                UDDIReplicationPortType replicationClient = svc.getUDDIReplicationPort();

                EntityManager em = PersistenceManager.getEntityManager();
                EntityTransaction tx = em.getTransaction();
                try {
                        StringBuilder sql = new StringBuilder();
                        sql.append("select c from ReplicationConfiguration c order by c.serialNumber desc");
                        sql.toString();
                        Query qry = em.createQuery(sql.toString());
                        qry.setMaxResults(1);

                        org.apache.juddi.model.ReplicationConfiguration resultList = (org.apache.juddi.model.ReplicationConfiguration) qry.getSingleResult();
                        for (Operator o : resultList.getOperator()) {
                                if (o.getOperatorNodeID().equalsIgnoreCase(node)) {
                                        ((BindingProvider) replicationClient).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, o.getSoapReplicationURL());

                                        return replicationClient;
                                }
                        }
                        tx.rollback();

                } catch (Exception ex) {
                        logger.fatal("Node not found (or there isn't a replication config)!" + node, ex);
                } finally {
                        if (tx.isActive()) {
                                tx.rollback();
                        }
                        em.close();
                }
                //em.close();
                return null;

        }

        /**
         * used to factor out the actual execution of custody transfer, used by
         * both this service and the replication service.
         *
         * @since 3.3
         * @param body
         * @param em
         * @param transferToPublisher
         * @param transferToNode
         * @return
         * @throws DispositionReportFaultMessage
         */
        protected List<ChangeRecord> executeTransfer(TransferEntities body, EntityManager em, String transferToPublisher, String transferToNode) throws DispositionReportFaultMessage {
                // Once validated, the ownership transfer is as simple as switching the publisher
                List<ChangeRecord> changes = new ArrayList<ChangeRecord>();;
                KeyBag keyBag = body.getKeyBag();
                List<String> keyList = keyBag.getKey();
                //used for the change journal

                for (String key : keyList) {
                        UddiEntity uddiEntity = em.find(UddiEntity.class, key);
                        uddiEntity.setAuthorizedName(transferToPublisher);
                        uddiEntity.setNodeId(transferToNode);
                        Date now = new Date();
                        uddiEntity.setModified(now);
                        uddiEntity.setModifiedIncludingChildren(now);

                        if (uddiEntity instanceof BusinessEntity) {
                                BusinessEntity be = (BusinessEntity) uddiEntity;

                                List<BusinessService> bsList = be.getBusinessServices();
                                for (BusinessService bs : bsList) {
                                        bs.setAuthorizedName(transferToPublisher);
                                        bs.setNodeId(transferToNode);
                                        bs.setModified(now);
                                        bs.setModifiedIncludingChildren(now);

                                        List<BindingTemplate> btList = bs.getBindingTemplates();
                                        for (BindingTemplate bt : btList) {
                                                bt.setAuthorizedName(transferToPublisher);
                                                bt.setNodeId(transferToNode);
                                                bt.setModified(now);
                                                bt.setModifiedIncludingChildren(now);

                                        }
                                }
                        }
                        ChangeRecord cr = new ChangeRecord();
                        cr.setChangeRecordNewData(new ChangeRecordNewData());
                        cr.getChangeRecordNewData().setOperationalInfo(new OperationalInfo());
                        cr.getChangeRecordNewData().getOperationalInfo().setAuthorizedName(transferToPublisher);
                        cr.getChangeRecordNewData().getOperationalInfo().setEntityKey(uddiEntity.getEntityKey());
                        cr.getChangeRecordNewData().getOperationalInfo().setNodeID(transferToNode);
                        GregorianCalendar gcal = new GregorianCalendar();
                        gcal.setTime(uddiEntity.getCreated());
                        cr.getChangeRecordNewData().getOperationalInfo().setCreated(df.newXMLGregorianCalendar(gcal));
                        gcal = new GregorianCalendar();
                        gcal.setTime(now);
                        cr.getChangeRecordNewData().getOperationalInfo().setModified(df.newXMLGregorianCalendar(gcal));
                        cr.getChangeRecordNewData().getOperationalInfo().setModifiedIncludingChildren(df.newXMLGregorianCalendar(gcal));
                        cr.getChangeRecordNewData().getOperationalInfo().setEntityKey(uddiEntity.getEntityKey());

                        if (uddiEntity instanceof BusinessEntity) {
                                cr.getChangeRecordNewData().setBusinessEntity(new org.uddi.api_v3.BusinessEntity());
                                MappingModelToApi.mapBusinessEntity((BusinessEntity) uddiEntity, cr.getChangeRecordNewData().getBusinessEntity());
                        }
                        if (uddiEntity instanceof Tmodel) {
                                cr.getChangeRecordNewData().setTModel(new org.uddi.api_v3.TModel());
                                MappingModelToApi.mapTModel((Tmodel) uddiEntity, cr.getChangeRecordNewData().getTModel());
                        }
                        changes.add(cr);
                        em.persist(uddiEntity);

                }

                // After transfer is finished, the token can be removed
                org.uddi.custody_v3.TransferToken apiTransferToken = body.getTransferToken();
                String transferTokenId;
                try {
                    transferTokenId = new String(apiTransferToken.getOpaqueToken(), UTF8);
                } catch (UnsupportedEncodingException ex) {
                    throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
                }
                org.apache.juddi.model.TransferToken modelTransferToken = em.find(org.apache.juddi.model.TransferToken.class, transferTokenId);
                em.remove(modelTransferToken);
                return changes;
        }
}