Skip to content

Write the usage sharing record in the specified form #28

Open

Description

@jwrosewell

Usage sharing is being aligned across every language to the record the .NET packages write, which draft specifications#65 describes byte for byte. The Java record, read at 40b615c, differs from it in these ways.

  1. Document. There is no byte order mark and no XML declaration, because writeStartDocument is never called, and the root is <Devices version="1.1"> where the specification has <Devices> (ShareUsageElement.java#L407-L409).
  2. Element order. Version, Product, FlowElement, Language, LanguageVersion, ServerIP and Platform come first, then SessionId, Sequence, DateSent and ClientIP. The specification starts with those last four (ShareUsageElement.java#L424-L451).
  3. SessionId and ClientIP are left out when the Evidence has no value for them, and an element whose content is empty is written <SessionId></SessionId>. The specification always writes both, and writes an empty one as <SessionId /> (ShareUsageElement.java#L476-L485).
  4. DateSent comes from a SimpleDateFormat with no time zone set, so it is the JVM's local time rather than UTC (ShareUsageBase.java#L63-L64, ShareUsageElement.java#L449).
  5. ServerIP is the local address of a UDP socket connected to 8.8.8.8, which is the address the machine would use to reach the internet, rather than the first suitable address the host name resolves to (ShareUsageBase.java#L491-L506).
  6. FlowElement holds the bare class name from getSimpleName() rather than the fully qualified name (ShareUsageBase.java#L464).
  7. Platform is os.name alone, such as Linux, with no operating system version (ShareUsageBase.java#L419).
  8. The three special keys are matched with case, so a key such as Query.Session-Id is treated as an ordinary query parameter and SessionId is left out (ShareUsageBase.java#L708-L727).
  9. Entries are grouped in hash maps, so prefixes and entries come out in hash order. The Evidence is also held sorted without regard to case, so the order it was given in is lost before grouping starts (ShareUsageBase.java#L810-L829, EvidenceDefault.java#L42).
  10. Values are written as escaped element text, never CDATA (ShareUsageElement.java#L469).
  11. Truncation cuts at 512 UTF-16 code units and marks the entry truncated="true", where the specification cuts at 10,000 behind the [TRUNCATED BY USAGE SHARING] prefix (Constants.java#L34, ShareUsageElement.java#L466-L468, ShareUsageElement.java#L518-L521).
  12. Character replacement turns every character outside U+0020 to U+007E and U+00A0 to U+00FF into U+FFFD, tab, line feed, carriage return and U+007F to U+009F included, and marks the entry replaced="true". Names are not checked. The specification replaces only characters from U+0000 to U+001F other than tab, line feed and carriage return, writes each as \x and four lowercase hex digits, and adds <BadSchema>true</BadSchema> (ShareUsageElement.java#L490-L502, ShareUsageElement.java#L522-L529, ShareUsageElement.java#L457-L465).
  13. The XML writer comes from XMLOutputFactory.newInstance(), which picks up any other XML writer library on the application's classpath, so how empty elements and escapes are written depends on the application (ShareUsageElement.java#L332).
  14. Request detail. The content type is text/xml; charset=utf-8 rather than text/xml, and a failure logs only the status code, because the response content is never read (ShareUsageElement.java#L328, ShareUsageElement.java#L395-L398).

Also found, outside the record writer:

  • The filter shares a query parameter only when it is in the allow list, so the specification's example entry query.51D_Test is not shared by default, where the specification also shares every query parameter starting 51D_ (EvidenceKeyFilterShareUsage.java#L134-L138).

What to change

Write the record exactly as the Processing section in specifications#65 describes, so the same Evidence gives the same bytes as the .NET packages, apart from DateSent and the values that name the runtime. Keeping the order the Evidence was given in changes the core Evidence type, which sorts its keys, not only this element, and the record needs an XML writer whose output does not depend on the classpath.

The pull request goes into the rc branch, not main, because changes are collected there and released together.

Done when

A test writes a document from fixed Evidence, the specification's example among it, and compares it byte for byte with the expected text. The cases include the byte order mark and declaration, an absent session id and client IP (<SessionId /> and <ClientIP />), a value of more than 10,000 UTF-16 code units, a control character, a value containing ]]>, keys whose order must be kept, a key with no ., and DateSent in UTC when the JVM's default time zone is not UTC.

The same change for the Rust crates is 51Degrees/51degrees-rust#232.

Activity

  1. jwrosewell commented on Nov 9, 2026

    @jwrosewell
    ContributorAuthor

    The record to write is the one in the Processing section of usage-sharing-element.md on rc, not the one this issue first pointed to. That draft, specifications#65, is closed and was not adopted.

    So a replaced character marks its element escaped="true", a shortened value marks it truncated="true", and there is no CDATA and no BadSchema. .NET keeps its current record for now, as reference-implementation-notes.md records, and moves to the specification's record later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions