docs: private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
          final boolean isTraceOn = TraceComponent.isAnyTracingEnabled();
          if (isTraceOn && tc.isEntryEnabled())
              Tr.entry(tc, "readObject");
  
          in.defaultReadObject();
  
          // Read in eye catcher.
          byte[] eyeCatcher = new byte[EYECATCHER.length];
  
          // Insure that all of the bytes have been read.
          int bytesRead = 0;
          for (int offset = 0; offset < EYECATCHER.length; offset += bytesRead) {
              bytesRead = in.read(eyeCatcher, offset, EYECATCHER.length - offset);
              if (bytesRead == -1) {
                  throw new IOException("end of input stream while reading eye catcher");
              }
          }
  
          // Validate that the eyecatcher matches
          for (int i = 0; i < EYECATCHER.length; i++) {
              if (EYECATCHER[i] != eyeCatcher[i]) {
                  String eyeCatcherString = new String(eyeCatcher);
                  throw new IOException("Invalid eye catcher '" + eyeCatcherString + "' in TimerHandle input stream");
              }
          }
  
          // Read in the rest of the header.
          @SuppressWarnings("unused")
          short incoming_platform = in.readShort();
          short incoming_vid = in.readShort();
  
          if (isTraceOn && tc.isDebugEnabled())
              Tr.debug(tc, "version = " + incoming_vid);
  
          // Verify the version is supported by this version of code.
          if (incoming_vid != Constants.TIMER_TASK_V1 &&
              incoming_vid != Constants.TIMER_TASK_V2) {
              throw new InvalidObjectException("EJB TimerTaskHandler data stream is not of the correct version, this client should be updated.");
          }
  
          // Read in the instance data.
  
          byte[] j2eeNameBytes = (byte[]) in.readObject();
          j2eeName = EJSContainer.j2eeNameFactory.create(j2eeNameBytes);
          userInfoBytes = (byte[]) in.readObject();
  
          switch (incoming_vid) {
  
              case Constants.TIMER_TASK_V1:
                  expiration = in.readLong();
                  interval = in.readLong();
                  break;
  
              case Constants.TIMER_TASK_V2:
                  parsedSchedule = (ParsedScheduleExpression) in.readObject();
                  methodId = in.readInt();
                  automaticMethodName = (String) in.readObject();
                  automaticClassName = (String) in.readObject();
                  break;
  
              default:
                  // cannot occur since unsupported version detected above
                  break;
          }
  
          if (isTraceOn && tc.isEntryEnabled())
              Tr.exit(tc, "readObject: " + this);
      }