Package org.xmlBlaster.engine.queuemsg

Examples of org.xmlBlaster.engine.queuemsg.MsgQueueUpdateEntry


         String subscriptionId = "subid";
         int redeliverCounter = 2;
         boolean updateOneway = true;
         org.xmlBlaster.engine.ServerScope global = new org.xmlBlaster.engine.ServerScope();
         MsgUnitWrapper msgWrapper = new MsgUnitWrapper(glob, msgUnit, queue.getStorageId());
         MsgQueueUpdateEntry entry = new MsgQueueUpdateEntry(global, msgWrapper, queue.getStorageId(),
                                         receiver, subscriptionId, updateOneway);
         entry.incrRedeliverCounter();
         entry.incrRedeliverCounter();

         queue.put(entry, false);
         I_QueueEntry returnEntry = queue.peek();

         boolean isUpdate = (returnEntry instanceof MsgQueueUpdateEntry);
         assertTrue("updateEntry: the return value is not an update ", isUpdate);
        
         MsgQueueUpdateEntry updateEntry = (MsgQueueUpdateEntry)returnEntry;

         assertEquals("The subscriptionId of the entry is different ", subscriptionId, updateEntry.getSubscriptionId());
         assertEquals("The state of the entry is different ", state, updateEntry.getState());
         assertEquals("The redeliverCounter of the entry is different ", redeliverCounter, updateEntry.getRedeliverCounter());
         assertEquals("The priority of the entry is different ", entry.getPriority(), updateEntry.getPriority());
         assertEquals("The oneway of the entry is different ", updateOneway, updateEntry.updateOneway());
         assertEquals("The persistent of the entry is different ", entry.isPersistent(), updateEntry.isPersistent());
         assertEquals("The receiver of the entry is different ", entry.getReceiver().toString(), updateEntry.getReceiver().toString());
         assertEquals("The uniqueId of the entry is different ", entry.getUniqueId(), updateEntry.getUniqueId());
         assertEquals("The msgUnitWrapperUniqueId of the entry is different ", entry.getMsgUnitWrapperUniqueId(), updateEntry.getMsgUnitWrapperUniqueId());
         assertEquals("The topic oid of the entry is different ", entry.getKeyOid(), updateEntry.getKeyOid());
         assertEquals("The topic oid of the entry is different ", entry.getStorageId().getId(), updateEntry.getStorageId().getId());
         log.info("SUCCESS: MsgQueueUpdateEntry: Persistent fields are read as expected");

         MsgUnit retMsgUnit = null;
         try {
            retMsgUnit = updateEntry.getMsgUnit();
         }
         catch (Throwable e) {  // Should not happen for RAM queue
            log.severe("Lookup failed, probably engine.Global has no Requestbroker, wi ignore the problem: " + e.getMessage());
            e.printStackTrace();
            return;
         }
         MsgQosData retMsgQosData = updateEntry.getMsgQosData();

         log.fine("Received" + retMsgQosData.toXml());

         // check message unit:
         assertEquals("The key of the message unit is different ", key.getOid(), retMsgUnit.getKeyData().getOid());
         assertEquals("The content of the message unit is different ", new String(retMsgUnit.getContent()), new String(content));
         //assertEquals("The qos of the message unit is different ", retMsgUnit.getQosData().isPersistent(), publishQosServer.isPersistent());
         //assertEquals("The qos of the message unit is different OLD="+oldXml+" NEW="+newXml, oldXml, newXml);

         assertEquals("msgQosData check failure: getSubscriptionId      ", msgQosData.getSubscriptionId(), retMsgQosData.getSubscriptionId());
//         assertEquals("msgQosData check failure: getPersistent             ", msgQosData.getPersistent(), retMsgQosData.getPersistent());
//         assertEquals("msgQosData check failure: getForceUpdate         ", msgQosData.getForceUpdate(), retMsgQosData.getForceUpdate());
//         assertEquals("msgQosData check failure: getReadOnly            ", msgQosData.getReadOnly(), retMsgQosData.getReadOnly());
         assertEquals("msgQosData check failure: getSender              ", msgQosData.getSender().toString(), retMsgQosData.getSender().toString());
         assertEquals("msgQosData check failure: getRedeliver           ", msgQosData.getRedeliver(), retMsgQosData.getRedeliver());
         assertEquals("msgQosData check failure: getQueueSize           ", msgQosData.getQueueSize(), retMsgQosData.getQueueSize());
         assertEquals("msgQosData check failure: getQueueIndex          ", msgQosData.getQueueIndex(), retMsgQosData.getQueueIndex());
         assertEquals("msgQosData check failure: getPriority            ", msgQosData.getPriority().getInt(), retMsgQosData.getPriority().getInt());
//         assertEquals("msgQosData check failure: getFromPersistentStore ", msgQosData.getFromPersistentStore(), retMsgQosData.getFromPersistentStore());
         assertEquals("msgQosData check failure: getLifeTime            ", msgQosData.getLifeTime(), retMsgQosData.getLifeTime());
         //assertEquals("msgQosData check failure: getRemainingLifeStatic ", msgQosData.getRemainingLifeStatic(), retMsgQosData.getRemainingLifeStatic());
         assertEquals("msgQosData check failure: receiver", receiver, updateEntry.getReceiver());

         queue.removeRandom(returnEntry); //just for cleaning up

         log.info("successfully completed tests for the updateEntry");
View Full Code Here


         if (mu == null)
            continue;
         MsgQosData msgQosData = (MsgQosData)mu.getQosData().clone();
         msgQosData.setTopicProperty(null);
         if (entry instanceof MsgQueueUpdateEntry) {
            MsgQueueUpdateEntry updateEntry = (MsgQueueUpdateEntry)entry;
            msgQosData.setState(updateEntry.getState());
            msgQosData.setSubscriptionId(updateEntry.getSubscriptionId());
         }
         msgQosData.setQueueIndex(i);
         msgQosData.setQueueSize(entries.length);
         if (msgQosData.getNumRouteNodes() == 1) {
            msgQosData.clearRoutes();
View Full Code Here

            // We are responsible to call destinationClient.getLock().release()
            final boolean returnLocked = true;
            destinationClient = authenticate.getOrCreateSubjectInfoByName(destination.getDestination(), returnLocked, null, null);
            try {
               MsgQueueUpdateEntry msgEntrySubject = new MsgQueueUpdateEntry(serverScope, cacheEntry,
                        destinationClient.getSubjectQueue().getStorageId(), destination.getDestination(),
                        Constants.SUBSCRIPTIONID_PtP, false);
               destinationClient.queueMessage(msgEntrySubject);
               continue;
            }
            finally {
               destinationClient.getLock().release();
            }
         }

         // Handle PtP to session in a thread safe manner
         SessionInfo receiverSessionInfo = null;
         try {
            receiverSessionInfo = authenticate.getSessionInfo(destination.getDestination());
            if (receiverSessionInfo != null) {
               receiverSessionInfo.getLock().lock();
               //receiverSessionInfo.waitUntilAlive();
               if (receiverSessionInfo.isAlive()) {
                  if (!receiverSessionInfo.getConnectQos().isPtpAllowed() &&
                      !Constants.EVENT_OID_ERASEDTOPIC.equals(cacheEntry.getKeyOid())) { // no spam, case 2
                     if (log.isLoggable(Level.FINE)) log.fine(ME+": Rejecting PtP message '" + cacheEntry.getLogId() + "' for destination [" + destination.getDestination() + "], isPtpAllowed=false");
                     throw new XmlBlasterException(serverScope, ErrorCode.USER_PTP_DENIED, ME,
                           receiverSessionInfo.getId() + " does not accept PtP messages '" + cacheEntry.getLogId() +
                           "' is rejected");
                  }
               }
               else {
                  receiverSessionInfo.releaseLockAssertOne("Topic=" + getId());
                  receiverSessionInfo = null;
               }
            }

            if (receiverSessionInfo == null && !forceQueing) {
               String tmp = ME+": Sending PtP message '" + cacheEntry.getLogId() + "' to '" + destination.getDestination() + "' failed, the destination is unkown, the message rejected.";
               log.warning(tmp);
               throw new XmlBlasterException(serverScope, ErrorCode.USER_PTP_UNKNOWNDESTINATION, ME, tmp +
                     " Client is not logged in and <destination forceQueuing='true'> is not set");
            }

            // Row 1 in table
            if (receiverSessionInfo == null) { // We create a faked session without password check
               if (log.isLoggable(Level.FINE)) log.fine(ME+": Working on PtP message '" + cacheEntry.getLogId() + "' for destination [" + destination.getDestination() + "] which does not exist, forceQueuing=true, we create a dummy session");
               ConnectQos connectQos = new ConnectQos(serverScope);
               connectQos.setSessionName(destinationSessionName);
               connectQos.setUserId(destinationSessionName.getLoginName());
               ConnectQosServer connectQosServer = new ConnectQosServer(serverScope, connectQos.getData());
               connectQosServer.bypassCredentialCheck(true);
               long sessionTimeout = serverScope.getProperty().get("session.ptp.defaultTimeout", -1L);
               connectQosServer.getSessionQos().setSessionTimeout(sessionTimeout)// Or use message timeout?
               for (int i=0; ; i++) {
                  if (i>=20) {
                     String tmp = "Sending PtP message '" + cacheEntry.getLogId() + "' to '" + destination.getDestination() + "' failed, the message is rejected.";
                     String status = "destinationIsSession='" + destinationIsSession + "'" +
                                     " forceQueing='" + forceQueing + "' wantsPtP='" + wantsPtP +"'";
                     throw new XmlBlasterException(serverScope, ErrorCode.INTERNAL_NOTIMPLEMENTED, ME, tmp +
                        "the combination '" + status + "' is not handled");
                  }
                  if (i>0) { try { Thread.sleep(1L); } catch( InterruptedException ie) {}}
                  /*ConnectReturnQosServer q = */authenticate.connect(connectQosServer);
                  receiverSessionInfo = authenticate.getSessionInfo(destination.getDestination());
                  if (receiverSessionInfo == null) continue;
                  receiverSessionInfo.getLock().lock();
                  if (!receiverSessionInfo.isAlive()) {
                     receiverSessionInfo.releaseLockAssertOne("Topic=" + getId());
                     receiverSessionInfo = null;
                     continue;
                  }
                  break;
               }
            }

            if (log.isLoggable(Level.FINE)) log.fine(ME+": Queuing PtP message '" + cacheEntry.getLogId() + "' for destination [" + destination.getDestination() + "]");
            MsgQueueUpdateEntry msgEntry = new MsgQueueUpdateEntry(serverScope,
                     cacheEntry,
                     receiverSessionInfo.getSessionQueue().getStorageId(),
                     destination.getDestination(),
                     Constants.SUBSCRIPTIONID_PtP, false);
            receiverSessionInfo.queueMessage(msgEntry);
View Full Code Here

                         sub.getSessionInfo().getSessionName() + "' threw an exception, we don't deliver " +
                         "the message to the subscriber: " + e.toString();
               if (log.isLoggable(Level.FINE)) log.fine(ME+": "+reason);
               if (handleException) {
                  MsgQueueEntry[] entries = {
                       new MsgQueueUpdateEntry(serverScope, msgUnitWrapper, sub.getMsgQueue().getStorageId(),
                                   sub.getSessionInfo().getSessionName(), sub.getSubSourceSubscriptionId(),
                                   sub.getSubscribeQosServer().getWantUpdateOneway()) };
                  requestBroker.deadMessage(entries, null, reason);
               }
View Full Code Here

      return false;
   }

   public static final MsgQueueUpdateEntry createEntryFromWrapper(MsgUnitWrapper msgUnitWrapper, SubscriptionInfo sub)
      throws XmlBlasterException {
      return new MsgQueueUpdateEntry(msgUnitWrapper.getServerScope(), msgUnitWrapper, sub.getMsgQueue().getStorageId(),
               sub.getSessionInfo().getSessionName(), sub.getSubSourceSubscriptionId(),
               sub.getSubscribeQosServer().getWantUpdateOneway());
   }
View Full Code Here

         }

         if (log.isLoggable(Level.FINER)) log.finer(ME+": pushing update() message '" + sub.getKeyData().getOid() + "' " + msgUnitWrapper.getStateStr() +
                       "' into '" + sub.getSessionInfo().getId() + "' callback queue");

         MsgQueueUpdateEntry entry = createEntryFromWrapper(msgUnitWrapper, sub);

         sub.getMsgQueue().put(entry, I_Queue.USE_PUT_INTERCEPTOR);

         I_Checkpoint cp = serverScope.getCheckpointPlugin();
         if (cp != null) {
               cp.passingBy(I_Checkpoint.CP_UPDATE_QUEUE_ADD, entry.getMsgUnit(),
                     sub.getSessionInfo().getSessionName(), null);
         }

         // If in MsgQueueUpdateEntry we set super.wantReturnObj = true; (see ReferenceEntry.java):
         //UpdateReturnQosServer retQos = (UpdateReturnQosServer)entry.getReturnObj();
         return 1;
      }
      catch (Throwable e) {
         SessionName publisherName = (publisherSessionInfo != null) ? publisherSessionInfo.getSessionName() :
                                     msgUnitWrapper.getMsgQosData().getSender();
         if ( doErrorHandling ) {
            if (log.isLoggable(Level.FINE)) log.fine(ME+": Sending of message from " + publisherName + " to " +
                               sub.getSessionInfo().getId() + " failed: " + e.toString());
            try {
               MsgQueueEntry[] entries = {
                     new MsgQueueUpdateEntry(serverScope, msgUnitWrapper, sub.getMsgQueue().getStorageId(),
                                 sub.getSessionInfo().getSessionName(), sub.getSubSourceSubscriptionId(),
                                 sub.getSubscribeQosServer().getWantUpdateOneway()) };
               String reason = e.toString();
               if (e instanceof XmlBlasterException)
                  reason = ((XmlBlasterException)e).getMessage();
View Full Code Here

               // put the current dispatcher at the end of the list for next invocation (round robin)
               subInfoList.remove(sub);
               subInfoList.add(sub);
              
               MsgQueueUpdateEntry updateEntry = TopicHandler.createEntryFromWrapper(msgUnitWrapper,sub);

               UpdateReturnQosServer retQos = doDistribute(sub, updateEntry);

               if (log.isLoggable(Level.FINE)) {
                  if (retQos == null) log.fine("distributeOneEntry: the return object was null: callback has not sent the message (dirty reads ?)");
View Full Code Here

         org.xmlBlaster.engine.ServerScope global = new org.xmlBlaster.engine.ServerScope();
         MsgUnitWrapper msgWrapper = new MsgUnitWrapper(glob, msgUnit, storageId);

         int step = 1000;
         int numCreate = 1000000;
         MsgQueueUpdateEntry entryArr[] = new MsgQueueUpdateEntry[numCreate];
         for(int i=0; i<numCreate; i++) {
            entryArr[i] = new MsgQueueUpdateEntry(global, msgWrapper, storageId,
                                             receiver, "__subId", false);
            MsgUnitWrapper w = entryArr[i].getMsgUnitWrapper();
            if ((i % step) == 0) {
               log.info("Overall created #" + i + ": Created " + step + " new MsgQueueUpdateEntry instances, hit a key to create more ...");
               try { System.in.read(); } catch(java.io.IOException e) {}
View Full Code Here

      ArrayList oneways = null;
      ArrayList responders = null;

      {
         for (int i=0; i<msgArr_.length; i++) {
            MsgQueueUpdateEntry entry = (MsgQueueUpdateEntry)msgArr_[i];

            MsgUnitWrapper msgUnitWrapper = entry.getMsgUnitWrapper();
            if (msgUnitWrapper == null) {
               if (log.isLoggable(Level.FINE)) log.fine(ME+": doSend("+entry.getLogId()+") ignoring callback message as no meat is available (assume expired)");
               entry.setReturnObj(new UpdateReturnQosServer(this.glob, Constants.RET_EXPIRED)); //"<qos><state id='EXPIRED'/></qos>";
               continue;
            }
            if (msgUnitWrapper.getMsgQosData().isPtp() && session!=null && !session.getConnectQos().isPtpAllowed()) {
               if (log.isLoggable(Level.FINE)) log.fine(ME+": doSend("+entry.getLogId()+") ignoring callback message as PtP is not wanted");
               entry.setReturnObj(new UpdateReturnQosServer(this.glob, Constants.RET_ERASED));
               continue;
            }

            MsgUnit mu = msgUnitWrapper.getMsgUnit();
            //MsgUnit mu = entry.getMsgUnit(); throws unwanted exception if meat==null (forceDestroy)
            MsgQosData msgQosData = (MsgQosData)mu.getQosData().clone();
            msgQosData.setTopicProperty(null);
            msgQosData.setState(entry.getState());
            msgQosData.setSubscriptionId(entry.getSubscriptionId());
            msgQosData.setQueueIndex(i);
            msgQosData.setQueueSize(connectionsHandler.getDispatchManager().getQueue().getNumOfEntries());
            if (msgQosData.getNumRouteNodes() == 1) {
               msgQosData.clearRoutes();
            }

            // Convert oid to original again for erased events fired by TopicHandler.java notifySubscribersAboutErase()
            if (mu.getKeyOid().equals(Constants.EVENT_OID_ERASEDTOPIC)) {
               mu = new MsgUnit(mu, (MsgKeyData)mu.getKeyData().clone(), null, msgQosData);
               String oid = mu.getQosData().getClientProperty("__oid", (String)null);
               if (oid != null) {
                  mu.getKeyData().setOid(oid);
                  try {
                     ((org.xmlBlaster.util.qos.MsgQosData)mu.getQosData()).setSubscriptionId(mu.getQosData().getClientProperty("__subscriptionId", (String)null));
                  }
                  catch (Throwable e) {
                     log.severe(ME+": Failed to set subscriptionId: " + e.toString());
                  }
                  String domain = mu.getQosData().getClientProperty("__domain", (String)null);
                  if (domain != null) {
                     mu.getKeyData().setDomain(domain);
                     mu.getQosData().getClientProperties().remove("__domain");
                  }
                  mu.getQosData().getClientProperties().remove("__oid");
                  mu.getQosData().getClientProperties().remove("__subscriptionId");
               }
            }
            else {
               mu = new MsgUnit(mu, null, null, msgQosData);
            }

            MsgUnitRaw raw = new MsgUnitRaw(mu, mu.getKeyData().toXml(), mu.getContent(), mu.getQosData().toXml());
            if (address.oneway() || entry.updateOneway()) {
               if (oneways == null) oneways = new ArrayList();
               oneways.add(new Holder(entry, raw, entry.getSubscriptionId()));
            }
            else {
               if (responders == null) responders = new ArrayList();
               responders.add(new Holder(entry, raw, entry.getSubscriptionId()));
            }
         }
      }

      exportCrypt(responders, MethodName.UPDATE);
      exportCrypt(oneways, MethodName.UPDATE_ONEWAY);

      if (oneways != null) {
         MsgUnitRaw[] raws = new MsgUnitRaw[oneways.size()];
         for (int i=0; i<oneways.size(); i++) {
            raws[i] = ((Holder)oneways.get(i)).msgUnitRaw;
         }
         cbDriver.sendUpdateOneway(raws);
         connectionsHandler.getDispatchStatistic().incrNumUpdate(oneways.size());
         if (log.isLoggable(Level.FINE)) log.fine(ME+": Success, sent " + oneways.size() + " oneway messages.");
         I_Checkpoint cp = glob.getCheckpointPlugin();
         if (cp != null) {
            for (int i=0; i<oneways.size(); i++) {
               Holder h = (Holder)oneways.get(i);
               cp.passingBy(I_Checkpoint.CP_UPDATE_ACK, (MsgUnit)h.msgUnitRaw.getMsgUnit(),
                     sessionName, null);
            }
         }
      }

      if (responders != null) {
         if (log.isLoggable(Level.FINE)) log.fine(ME+": Before update " + responders.size() + " acknowledged messages ...");
         MsgUnitRaw[] raws = new MsgUnitRaw[responders.size()];
         for (int i=0; i<responders.size(); i++) {
            raws[i] = ((Holder)responders.get(i)).msgUnitRaw;
         }
         String[] rawReturnVal = null;
         try {
            rawReturnVal = cbDriver.sendUpdate(raws);
         }
         catch (Throwable t) {
            // http://www.xmlblaster.org/xmlBlaster/doc/requirements/interface.update.html#exception
            XmlBlasterException ex = (t instanceof XmlBlasterException) ? (XmlBlasterException)t :
               new XmlBlasterException(glob, ErrorCode.USER_UPDATE_INTERNALERROR, ME, "Callback failed", t);
            if (!ex.isServerSide()) { // Transform remote exceptions must be of type user.* or communication.*
               if (!ex.isUser() && !ex.isCommunication())
                  ex = new XmlBlasterException(glob, ErrorCode.USER_UPDATE_INTERNALERROR, ME, "Callback failed", ex);
            }
            throw ex;
         }
         connectionsHandler.getDispatchStatistic().incrNumUpdate(raws.length);
         if (log.isLoggable(Level.FINE)) log.fine(ME+": Success, sent " + raws.length + " acknowledged messages, return value #1 is '" + rawReturnVal[0] + "'");

         I_Checkpoint cp = glob.getCheckpointPlugin();
         if (cp != null) {
            for (int i=0; i<raws.length; i++) {
               cp.passingBy(I_Checkpoint.CP_UPDATE_ACK, (MsgUnit)raws[i].getMsgUnit(),
                     sessionName, null);
            }
         }

         // this is done since the client could send one single bulk acknowledge
         if (rawReturnVal != null && rawReturnVal.length == 1 && raws.length > 1) {
            String bulkReturnValue = rawReturnVal[0];
            log.fine("Reconstructing return values of a bulk acknowledge '" + bulkReturnValue + "'");
            rawReturnVal = new String[raws.length];
            for (int i=0; i < rawReturnVal.length; i++)
               rawReturnVal[i] = bulkReturnValue;
         }

         if (rawReturnVal != null && rawReturnVal.length == raws.length) {
            I_MsgSecurityInterceptor securityInterceptor = connectionsHandler.getDispatchManager().getMsgSecurityInterceptor();
            for (int i=0; i<rawReturnVal.length; i++) {
               MsgQueueUpdateEntry entry = ((Holder)responders.get(i)).msgQueueUpdateEntry;
               if (!entry.wantReturnObj())
                  continue;

               if (securityInterceptor != null) {
                  // decrypt ...
                  CryptDataHolder dataHolder = new CryptDataHolder(MethodName.UPDATE,
                        new MsgUnitRaw(null, (byte[])null, rawReturnVal[i]));
                  dataHolder.setReturnValue(true);
                  rawReturnVal[i] = securityInterceptor.importMessage(dataHolder).getQos();
               }

               // create object
               try {
                  entry.setReturnObj(new UpdateReturnQosServer(glob, rawReturnVal[i]));
               }
               catch (Throwable e) {
                  log.warning(ME+": Can't parse returned value '" + rawReturnVal[i] + "', setting to default: " + e.toString());
                  //e.printStackTrace();
                  UpdateReturnQosServer updateRetQos = new UpdateReturnQosServer(glob, "<qos/>");
                  updateRetQos.setException(e);
                  entry.setReturnObj(updateRetQos);
               }
            }
            if (log.isLoggable(Level.FINE)) log.fine(ME+": Imported/decrypted " + rawReturnVal.length + " message return values.");
         }
         else
View Full Code Here

   public ArrayList filterDistributorEntries(ArrayList entries, Throwable ex) {
      ArrayList entriesWithNoDistributor = new ArrayList();
      for (int i=0; i < entries.size(); i++) {
         Object obj = entries.get(i);
         if (!(obj instanceof MsgQueueUpdateEntry)) return entries; // Can be removed, was to distinguish client side code which is not necessary anymore (as we are the server side implementation)
         MsgQueueUpdateEntry entry = (MsgQueueUpdateEntry)obj;
         MsgUnitWrapper wrapper = entry.getMsgUnitWrapper();
         boolean hasMsgDistributor = wrapper.getServerScope().getTopicAccessor().hasMsgDistributorPluginDirtyRead(wrapper.getKeyOid());
        
         if (hasMsgDistributor) {
            if (ex != null) { // in this case it is possible that retObj is not set yet
               UpdateReturnQosServer retQos = (UpdateReturnQosServer)entry.getReturnObj();              
               try {
                  if (retQos == null) {
                     retQos = new UpdateReturnQosServer(this.glob, "<qos/>");
                     entry.setReturnObj(retQos);
                  }   
                  retQos.setException(ex);
               }
               catch (XmlBlasterException ee) {
                  log.severe("filterDistributorEntries: " + ee.getMessage());
View Full Code Here

TOP

Related Classes of org.xmlBlaster.engine.queuemsg.MsgQueueUpdateEntry

Copyright © 2018 www.massapicom. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.