View Javadoc
1   /*
2    * Copyright 2001-2008 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  package org.apache.juddi.api.impl;
18  
19  import java.io.UnsupportedEncodingException;
20  import java.util.ArrayList;
21  import java.util.Date;
22  import java.util.GregorianCalendar;
23  import java.util.List;
24  import java.util.UUID;
25  import java.util.Vector;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.jws.WebService;
30  import javax.persistence.EntityManager;
31  import javax.persistence.EntityTransaction;
32  import javax.persistence.Query;
33  import javax.xml.datatype.DatatypeConfigurationException;
34  import javax.xml.datatype.DatatypeFactory;
35  import javax.xml.datatype.XMLGregorianCalendar;
36  import javax.xml.ws.BindingProvider;
37  import javax.xml.ws.Holder;
38  
39  import org.apache.commons.configuration.ConfigurationException;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.juddi.api.util.CustodyTransferQuery;
43  import org.apache.juddi.api.util.QueryStatus;
44  import org.apache.juddi.config.AppConfig;
45  import org.apache.juddi.config.PersistenceManager;
46  import org.apache.juddi.config.Property;
47  import org.apache.juddi.mapping.MappingApiToModel;
48  import org.apache.juddi.mapping.MappingModelToApi;
49  import org.apache.juddi.model.BindingTemplate;
50  import org.apache.juddi.model.BusinessEntity;
51  import org.apache.juddi.model.BusinessService;
52  import org.apache.juddi.model.Operator;
53  import org.apache.juddi.model.Tmodel;
54  import org.apache.juddi.model.TransferTokenKey;
55  import org.apache.juddi.model.UddiEntity;
56  import org.apache.juddi.model.UddiEntityPublisher;
57  import org.apache.juddi.query.util.DynamicQuery;
58  import org.apache.juddi.replication.ReplicationNotifier;
59  import org.apache.juddi.v3.client.UDDIService;
60  import org.apache.juddi.v3.error.ErrorMessage;
61  import org.apache.juddi.v3.error.FatalErrorException;
62  import org.apache.juddi.v3.error.InvalidValueException;
63  import org.apache.juddi.v3.error.TransferNotAllowedException;
64  import org.apache.juddi.validation.ValidateCustodyTransfer;
65  import org.uddi.api_v3.OperationalInfo;
66  import org.uddi.custody_v3.DiscardTransferToken;
67  import org.uddi.custody_v3.KeyBag;
68  import org.uddi.custody_v3.TransferEntities;
69  import org.uddi.custody_v3.TransferOperationalInfo;
70  import org.uddi.repl_v3.ChangeRecord;
71  import org.uddi.repl_v3.ChangeRecordIDType;
72  import org.uddi.repl_v3.ChangeRecordNewData;
73  import org.uddi.repl_v3.TransferCustody;
74  import org.uddi.v3_service.DispositionReportFaultMessage;
75  import org.uddi.v3_service.UDDICustodyTransferPortType;
76  import org.uddi.v3_service.UDDIReplicationPortType;
77  
78  /**
79   * This implements the UDDI v3 Custody Transfer API web service
80   *
81   */
82  @WebService(serviceName = "UDDICustodyTransferService",
83          endpointInterface = "org.uddi.v3_service.UDDICustodyTransferPortType",
84          targetNamespace = "urn:uddi-org:v3_service")
85  public class UDDICustodyTransferImpl extends AuthenticatedService implements UDDICustodyTransferPortType {
86  
87          public static final String TRANSFER_TOKEN_PREFIX = "transfertoken:";
88          public static final int DEFAULT_TRANSFEREXPIRATION_DAYS = 3;
89  
90          private static Log logger = LogFactory.getLog(UDDICustodyTransferImpl.class);
91  
92          private static DatatypeFactory df = null;
93          private UDDIServiceCounter serviceCounter;
94  
95          public UDDICustodyTransferImpl() {
96                  super();
97                  serviceCounter = ServiceCounterLifecycleResource.getServiceCounter(this.getClass());
98                  init();
99          }
100         
101         private static synchronized void init() {
102                 if (df == null) {
103                         try {
104                                 df = DatatypeFactory.newInstance();
105                         } catch (DatatypeConfigurationException ex) {
106                                 Logger.getLogger(UDDICustodyTransferImpl.class.getName()).log(Level.SEVERE, null, ex);
107                         }
108                 }
109         }
110 
111         @SuppressWarnings("unchecked")
112         @Override
113         public void discardTransferToken(DiscardTransferToken body)
114                 throws DispositionReportFaultMessage {
115                 long startTime = System.currentTimeMillis();
116 
117                 EntityManager em = PersistenceManager.getEntityManager();
118                 EntityTransaction tx = em.getTransaction();
119                 try {
120                         tx.begin();
121 
122                         UddiEntityPublisher publisher = this.getEntityPublisher(em, body.getAuthInfo());
123 
124                         new ValidateCustodyTransfer(publisher).validateDiscardTransferToken(em, body);
125 
126                         org.uddi.custody_v3.TransferToken apiTransferToken = body.getTransferToken();
127                         if (apiTransferToken != null) {
128                                 String transferTokenId;
129                                 try {
130                                     transferTokenId = new String(apiTransferToken.getOpaqueToken(), UTF8);
131                                 } catch (UnsupportedEncodingException ex) {
132                                     throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
133                                 }
134                                 org.apache.juddi.model.TransferToken modelTransferToken = em.find(org.apache.juddi.model.TransferToken.class, transferTokenId);
135                                 if (modelTransferToken != null) {
136                                         em.remove(modelTransferToken);
137                                 }
138                         }
139 
140                         KeyBag keyBag = body.getKeyBag();
141                         if (keyBag != null) {
142                                 List<String> keyList = keyBag.getKey();
143                                 Vector<DynamicQuery.Parameter> params = new Vector<DynamicQuery.Parameter>(0);
144                                 for (String key : keyList) {
145                                         // Creating parameters for key-checking query
146                                         DynamicQuery.Parameter param = new DynamicQuery.Parameter("UPPER(ttk.entityKey)",
147                                                 key.toUpperCase(),
148                                                 DynamicQuery.PREDICATE_EQUALS);
149 
150                                         params.add(param);
151                                 }
152 
153                                 // Find the associated transfer tokens and remove them.
154                                 DynamicQuery getTokensQry = new DynamicQuery();
155                                 getTokensQry.append("select distinct ttk.transferToken from TransferTokenKey ttk").pad();
156                                 getTokensQry.WHERE().pad().appendGroupedOr(params.toArray(new DynamicQuery.Parameter[0]));
157 
158                                 Query qry = getTokensQry.buildJPAQuery(em);
159                                 List<org.apache.juddi.model.TransferToken> tokensToDelete = qry.getResultList();
160                                 if (tokensToDelete != null && tokensToDelete.size() > 0) {
161                                         for (org.apache.juddi.model.TransferToken tt : tokensToDelete) {
162                                                 em.remove(tt);
163                                         }
164                                 }
165                         }
166 
167                         tx.commit();
168                         long procTime = System.currentTimeMillis() - startTime;
169                         serviceCounter.update(CustodyTransferQuery.DISCARD_TRANSFERTOKEN,
170                                 QueryStatus.SUCCESS, procTime);
171 
172                 } finally {
173                         if (tx.isActive()) {
174                                 tx.rollback();
175                         }
176                         em.close();
177                 }
178         }
179 
180         @Override
181         public void getTransferToken(String authInfo, KeyBag keyBag,
182                 Holder<String> nodeID, Holder<XMLGregorianCalendar> expirationTime,
183                 Holder<byte[]> opaqueToken) throws DispositionReportFaultMessage {
184                 long startTime = System.currentTimeMillis();
185 
186                 EntityManager em = PersistenceManager.getEntityManager();
187                 EntityTransaction tx = em.getTransaction();
188                 try {
189                         tx.begin();
190 
191                         UddiEntityPublisher publisher = this.getEntityPublisher(em, authInfo);
192 
193                         new ValidateCustodyTransfer(publisher).validateGetTransferToken(em, keyBag);
194 
195                         int transferExpirationDays = DEFAULT_TRANSFEREXPIRATION_DAYS;
196                         try {
197                                 transferExpirationDays = AppConfig.getConfiguration().getInt(Property.JUDDI_TRANSFER_EXPIRATION_DAYS);
198                                 // For output
199                                 nodeID.value = AppConfig.getConfiguration().getString(Property.JUDDI_NODE_ID);
200                         } catch (ConfigurationException ce) {
201                                 throw new FatalErrorException(new ErrorMessage("errors.configuration.Retrieval"));
202                         }
203 
204                         String transferKey = TRANSFER_TOKEN_PREFIX + UUID.randomUUID();
205                         org.apache.juddi.model.TransferToken transferToken = new org.apache.juddi.model.TransferToken();
206                         transferToken.setTransferToken(transferKey);
207                         try {
208                             // For output
209                             opaqueToken.value = transferKey.getBytes(UTF8);
210                         } catch (UnsupportedEncodingException ex) {
211                             throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
212                         }
213 
214                         GregorianCalendar gc = new GregorianCalendar();
215                         gc.add(GregorianCalendar.DAY_OF_MONTH, transferExpirationDays);
216 
217                         transferToken.setExpirationDate(gc.getTime());
218 
219                         try {
220                                 DatatypeFactory df = DatatypeFactory.newInstance();
221                                 // For output
222                                 expirationTime.value = df.newXMLGregorianCalendar(gc);
223                         } catch (DatatypeConfigurationException ce) {
224                                 throw new FatalErrorException(new ErrorMessage("errors.Unspecified"));
225                         }
226 
227                         List<String> keyList = keyBag.getKey();
228                         for (String key : keyList) {
229                                 TransferTokenKey tokenKey = new TransferTokenKey(transferToken, key);
230                                 transferToken.getTransferKeys().add(tokenKey);
231                         }
232 
233                         em.persist(transferToken);
234 
235                         tx.commit();
236 
237                         long procTime = System.currentTimeMillis() - startTime;
238                         serviceCounter.update(CustodyTransferQuery.GET_TRANSFERTOKEN,
239                                 QueryStatus.SUCCESS, procTime);
240 
241                 } finally {
242                         if (tx.isActive()) {
243                                 tx.rollback();
244                         }
245                         em.close();
246                 }
247         }
248 
249         @Override
250         public void transferEntities(TransferEntities body)
251                 throws DispositionReportFaultMessage {
252                 long startTime = System.currentTimeMillis();
253 
254                 EntityManager em = PersistenceManager.getEntityManager();
255                 EntityTransaction tx = em.getTransaction();
256                 List<ChangeRecord> changes = new ArrayList<ChangeRecord>();
257                 try {
258                         tx.begin();
259 
260                         UddiEntityPublisher publisher = this.getEntityPublisher(em, body.getAuthInfo());
261 
262                         ValidateCustodyTransfer verifier = new ValidateCustodyTransfer(publisher);
263 
264                         //if the destination transfer is to a different node, 
265                         if (!verifier.validateTransferEntities(em, body)) {
266                                 //i don't own these entities, so tell the owner to transfer to me.
267 
268                                 //look up the replication config endpoint for that node and trigger the transfer, then return
269                                 //ok this is a node to node transfer, first up a replication client to the destination node
270                                 String sourceNode = null;
271                                 try {
272                                         KeyBag keyBag = body.getKeyBag();
273                                         List<String> keyList = keyBag.getKey();
274                                         for (String key : keyList) {
275                                                 UddiEntity uddiEntity = em.find(UddiEntity.class, key);
276                                                 
277                                                 if (uddiEntity!=null) {
278                                                         uddiEntity.setIsTransferInProgress(true);
279                                                         sourceNode = uddiEntity.getNodeId();
280                                                         em.merge(uddiEntity);
281                                                         //save the fact we are expecting a transfer
282                                                 }
283                                                 else
284                                                 {
285                                                         logger.warn("couldn't find a record for key " + key);
286                                                 }
287                                         }
288                                         if (sourceNode==null){
289                                                 logger.warn("unable to process transfer, could not locate the source node, perhaps it hasn't been replicated to this node yet?")
290                                                         ;
291                                                 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?");
292                                         }
293 
294                                         UDDIReplicationPortType replicationClient = getReplicationClient(sourceNode);
295                                         if (replicationClient == null) {
296                                                 throw new Exception("Unknown node. is it in the replication graph?" + sourceNode);
297                                         }
298                                         TransferCustody transferCustody = new TransferCustody();
299                                         transferCustody.setTransferToken(body.getTransferToken());
300                                         transferCustody.setKeyBag(body.getKeyBag());
301                                         transferCustody.setTransferOperationalInfo(new TransferOperationalInfo());
302                                         transferCustody.getTransferOperationalInfo().setAuthorizedName(publisher.getAuthorizedName());
303                                         transferCustody.getTransferOperationalInfo().setNodeID(getNode());
304 
305                                         //and trigger the transfer
306                                         logger.info("AUDIT, transfering " + transferCustody.getKeyBag().getKey().size() + " entities to " + publisher.getAuthorizedName() + " at node " + getNode() + " from source " + sourceNode);
307                                         replicationClient.transferCustody(transferCustody);
308                                 } catch (DispositionReportFaultMessage df) {
309                                         logger.error("Unable to transfer entities from " + sourceNode + " to node " + getNode() + " to user " + publisher.getAuthorizedName(), df);
310                                         throw new TransferNotAllowedException(new ErrorMessage("E_transferBlocked", df.getMessage()));
311                                 } catch (Exception ex) {
312                                         logger.error("Unable to transfer entities from " + sourceNode + " to node " + getNode() + " to user " + publisher.getAuthorizedName(), ex);
313                                         throw new TransferNotAllowedException(new ErrorMessage("E_transferBlocked", ex.getMessage()));
314                                 }
315 
316                         } else {
317                                 changes.addAll(executeTransfer(body, em, publisher.getAuthorizedName(), getNode()));
318                                 //all of the items to be transfer are owned locally by *this node.
319 
320                         }
321                         tx.commit();
322                         //we need to do something for replication purposes here
323                         //enqueue notifications and storage of the changed records
324                         for (ChangeRecord c : changes) {
325                                 try {
326                                         c.setChangeID(new ChangeRecordIDType());
327                                         c.getChangeID().setNodeID(getNode());
328                                         c.getChangeID().setOriginatingUSN(null);
329                                         ReplicationNotifier.enqueue(MappingApiToModel.mapChangeRecord(c));
330                                 } catch (UnsupportedEncodingException ex) {
331                                         logger.error("", ex);
332                                 }
333                         }
334                         long procTime = System.currentTimeMillis() - startTime;
335                         serviceCounter.update(CustodyTransferQuery.TRANSFER_ENTITIES,
336                                 QueryStatus.SUCCESS, procTime);
337 
338                 } finally {
339                         if (tx.isActive()) {
340                                 tx.rollback();
341                         }
342                         em.close();
343                 }
344 
345         }
346 
347         private synchronized UDDIReplicationPortType getReplicationClient(String node) {
348 
349                 UDDIService svc = new UDDIService();
350                 UDDIReplicationPortType replicationClient = svc.getUDDIReplicationPort();
351 
352                 EntityManager em = PersistenceManager.getEntityManager();
353                 EntityTransaction tx = em.getTransaction();
354                 try {
355                         StringBuilder sql = new StringBuilder();
356                         sql.append("select c from ReplicationConfiguration c order by c.serialNumber desc");
357                         sql.toString();
358                         Query qry = em.createQuery(sql.toString());
359                         qry.setMaxResults(1);
360 
361                         org.apache.juddi.model.ReplicationConfiguration resultList = (org.apache.juddi.model.ReplicationConfiguration) qry.getSingleResult();
362                         for (Operator o : resultList.getOperator()) {
363                                 if (o.getOperatorNodeID().equalsIgnoreCase(node)) {
364                                         ((BindingProvider) replicationClient).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, o.getSoapReplicationURL());
365 
366                                         return replicationClient;
367                                 }
368                         }
369                         tx.rollback();
370 
371                 } catch (Exception ex) {
372                         logger.fatal("Node not found (or there isn't a replication config)!" + node, ex);
373                 } finally {
374                         if (tx.isActive()) {
375                                 tx.rollback();
376                         }
377                         em.close();
378                 }
379                 //em.close();
380                 return null;
381 
382         }
383 
384         /**
385          * used to factor out the actual execution of custody transfer, used by
386          * both this service and the replication service.
387          *
388          * @since 3.3
389          * @param body
390          * @param em
391          * @param transferToPublisher
392          * @param transferToNode
393          * @return
394          * @throws DispositionReportFaultMessage
395          */
396         protected List<ChangeRecord> executeTransfer(TransferEntities body, EntityManager em, String transferToPublisher, String transferToNode) throws DispositionReportFaultMessage {
397                 // Once validated, the ownership transfer is as simple as switching the publisher
398                 List<ChangeRecord> changes = new ArrayList<ChangeRecord>();;
399                 KeyBag keyBag = body.getKeyBag();
400                 List<String> keyList = keyBag.getKey();
401                 //used for the change journal
402 
403                 for (String key : keyList) {
404                         UddiEntity uddiEntity = em.find(UddiEntity.class, key);
405                         uddiEntity.setAuthorizedName(transferToPublisher);
406                         uddiEntity.setNodeId(transferToNode);
407                         Date now = new Date();
408                         uddiEntity.setModified(now);
409                         uddiEntity.setModifiedIncludingChildren(now);
410 
411                         if (uddiEntity instanceof BusinessEntity) {
412                                 BusinessEntity be = (BusinessEntity) uddiEntity;
413 
414                                 List<BusinessService> bsList = be.getBusinessServices();
415                                 for (BusinessService bs : bsList) {
416                                         bs.setAuthorizedName(transferToPublisher);
417                                         bs.setNodeId(transferToNode);
418                                         bs.setModified(now);
419                                         bs.setModifiedIncludingChildren(now);
420 
421                                         List<BindingTemplate> btList = bs.getBindingTemplates();
422                                         for (BindingTemplate bt : btList) {
423                                                 bt.setAuthorizedName(transferToPublisher);
424                                                 bt.setNodeId(transferToNode);
425                                                 bt.setModified(now);
426                                                 bt.setModifiedIncludingChildren(now);
427 
428                                         }
429                                 }
430                         }
431                         ChangeRecord cr = new ChangeRecord();
432                         cr.setChangeRecordNewData(new ChangeRecordNewData());
433                         cr.getChangeRecordNewData().setOperationalInfo(new OperationalInfo());
434                         cr.getChangeRecordNewData().getOperationalInfo().setAuthorizedName(transferToPublisher);
435                         cr.getChangeRecordNewData().getOperationalInfo().setEntityKey(uddiEntity.getEntityKey());
436                         cr.getChangeRecordNewData().getOperationalInfo().setNodeID(transferToNode);
437                         GregorianCalendar gcal = new GregorianCalendar();
438                         gcal.setTime(uddiEntity.getCreated());
439                         cr.getChangeRecordNewData().getOperationalInfo().setCreated(df.newXMLGregorianCalendar(gcal));
440                         gcal = new GregorianCalendar();
441                         gcal.setTime(now);
442                         cr.getChangeRecordNewData().getOperationalInfo().setModified(df.newXMLGregorianCalendar(gcal));
443                         cr.getChangeRecordNewData().getOperationalInfo().setModifiedIncludingChildren(df.newXMLGregorianCalendar(gcal));
444                         cr.getChangeRecordNewData().getOperationalInfo().setEntityKey(uddiEntity.getEntityKey());
445 
446                         if (uddiEntity instanceof BusinessEntity) {
447                                 cr.getChangeRecordNewData().setBusinessEntity(new org.uddi.api_v3.BusinessEntity());
448                                 MappingModelToApi.mapBusinessEntity((BusinessEntity) uddiEntity, cr.getChangeRecordNewData().getBusinessEntity());
449                         }
450                         if (uddiEntity instanceof Tmodel) {
451                                 cr.getChangeRecordNewData().setTModel(new org.uddi.api_v3.TModel());
452                                 MappingModelToApi.mapTModel((Tmodel) uddiEntity, cr.getChangeRecordNewData().getTModel());
453                         }
454                         changes.add(cr);
455                         em.persist(uddiEntity);
456 
457                 }
458 
459                 // After transfer is finished, the token can be removed
460                 org.uddi.custody_v3.TransferToken apiTransferToken = body.getTransferToken();
461                 String transferTokenId;
462                 try {
463                     transferTokenId = new String(apiTransferToken.getOpaqueToken(), UTF8);
464                 } catch (UnsupportedEncodingException ex) {
465                     throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
466                 }
467                 org.apache.juddi.model.TransferToken modelTransferToken = em.find(org.apache.juddi.model.TransferToken.class, transferTokenId);
468                 em.remove(modelTransferToken);
469                 return changes;
470         }
471 }