Migrations - phax/phase4 GitHub Wiki
This page contains everything necessary related to migrating phase4 versions
- The usage of the configuration file
phase4.propertiesandprivate-phase4.propertieswas removed. Useapplication.propertiesorprivate-application.properties, environment variables or system properties instead.
v4.8.0 changed the way the details of the TLS connection of a single AS4 message are surfaced, and it updated to WSS4J 4.0.2. The first two items below require source changes, and the WSS4J update affects interoperability with partners using AES-256, so v4.8.0 is not a drop-in replacement for v4.7.x.
-
The TLS related
Consumerparameters now take anAS4ConnectionDetailsinstead of anICommonsList<X509Certificate>. This affects the last parameter ofsendGenericMessage(...)andsendGenericMessageWithRetries(...)inBasicHttpPosterandIHttpPoster, as well as the second parameter of theAS4ClientSentMessageconstructors. The new class carries the HTTP version and the IP addresses of both ends plus a nestedAS4TlsConnectionDetailswith the certificates, the negotiated TLS protocol version, the cipher suite, the key size, the TLS session ID, the SNI and the principals.AS4ClientSentMessage.getRemoteTlsPeerCerts()is unchanged and now returnsgetConnectionDetails().getTlsDetails().peerCerts() -
Custom implementations of
IAS4IncomingMessageMetadataneed to implement the new methodsgetTlsConnectionDetails(),getLocalAddr(),getLocalPort(),getServerName(),getHttpVersion()andgetSecure().AS4IncomingMessageMetadatahas the matching setters;getSecure()returns anETriStateand must never returnnull -
Incoming messages encrypted with AES-256 are rejected if the session key is only 128 bits long.
WSS4J 4.0.2 rejects a symmetric key whose length does not exactly match the declared encryption algorithm - previously a 128-bit key was silently accepted for
aes256-gcmand effectively used as AES-128. Up to and including v4.7.x, phase4 itself sent exactly such messages: the session key was 128 bits long by default, even ifaes256-cbcoraes256-gcmwas configured, unless a session key provider was set explicitly (see #401). Of the shipped profiles only DBNAlliance uses AES-256 (aes256-gcm), butPhase4DBNAllianceSenderhas always explicitly used a 256-bit session key (ICryptoSessionKeyProvider.INSTANCE_RANDOM_AES_256), so DBNAlliance messages sent with it by older phase4 versions are not affected. Affected are only messages sent by phase4 v4.7.x or older with an AES-256 algorithm but without an explicit session key provider - e.g. a custom PMode or a sender builder other thanPhase4DBNAllianceSender- they fail withEBMS:0102(Failed Decryption) at a receiver on v4.8.0. Such senders must either set a 256-bit session key provider explicitly or update to phase4 v4.8.0 or later. The opposite direction is not affected: the correct 256-bit session keys sent by v4.8.0 are accepted by older versions as well. All other shipped profiles useaes128-gcmand are not affected - WSS4J 4.0.2 brings the new transitive dependency
org.apache.commons:commons-secure-xml. Nothing needs to be done, unless you manage dependencies explicitly (e.g. an allowlist, a shaded JAR or an OSGi setup)
All items of this section are consequences of the update to peppol-commons 13.0.0, which moved the network neutral types out of the Peppol specific packages and modules, so that Peppol, DBNAlliance, HR eDelivery and future networks can share them. Except for the Country C1 check mentioned last, none of them changes the AS4 behaviour of phase4 - but they do require source changes, so v4.7.0 is not a drop-in replacement for v4.6.x.
-
ISMPTransportProfilemoved fromcom.helger.peppol.smptocom.helger.edelivery.smp. Change the import of that type. It is used as the type of the constantDEFAULT_TRANSPORT_PROFILEand in the methodsgetTransportProfile()andsetTransportProfile(ISMPTransportProfile)ofAS4EndpointDetailProviderBDXR,AS4EndpointDetailProviderBDXR2,AS4EndpointDetailProviderPeppolandPhase4PeppolServletMessageProcessorSPI. The Peppol enumESMPTransportProfileitself stays incom.helger.peppol.smp -
The transport profiles of the non-Peppol networks are no longer constants of the Peppol enum
ESMPTransportProfile. ReplaceESMPTransportProfile.TRANSPORT_PROFILE_DBNA_AS4_V1withEDBNAllianceTransportProfile.AS4_V1(packagecom.helger.dbnalliance.commons.smp) andESMPTransportProfile.TRANSPORT_PROFILE_ERACUN_AS4_V1withEHREDeliveryTransportProfile.AS4_V1(packagecom.helger.hredelivery.commons.smp). The type ofPhase4DBNAllianceServletMessageProcessorSPI.DEFAULT_TRANSPORT_PROFILEand ofPhase4HREDeliveryServletMessageProcessorSPI.DEFAULT_TRANSPORT_PROFILEchanged accordingly. The transport profile IDs are unchanged, so nothing changes on the wire or in an SMP -
The DNS based SMP URL providers moved from
com.helger.smpclient.urltocom.helger.smpclient.url.dns. This affectsBDXLURLProvider,IBDXLURLProvider,PeppolNaptrURLProviderandDBNAURLProviderSMP, and therefore the imports needed aroundPhase4PeppolSender.URL_PROVIDER,Phase4CEFSender.URL_PROVIDER,Phase4EDelivery2Sender.URL_PROVIDERandPhase4EudamedSender.URL_PROVIDER.ISMPURLProviderandSMPDNSResolutionExceptionstay incom.helger.smpclient.url -
IPeppolURLProviderwas deprecated in favour ofIBDXLURLProvider, soPhase4PeppolSender.URL_PROVIDERis now declared asIBDXLURLProvider. If you assigned that constant to a variable, a field or a parameter of typeIPeppolURLProvider, change that type toIBDXLURLProvider -
Implementations of
IPhase4DBNAllianceIncomingXHEHandlerSPIneed to change one import:DBNAllianceXHEDatamoved fromcom.helger.peppol.xhetocom.helger.dbnalliance.xhe. The class name, its API and the rest of the method signature are unchanged. The same move applies toDBNAlliancePayload,DBNAllianceXHEDataReader,DBNAllianceXHEDataWriterandDBNAllianceXHEDataReadException - If you reference
MozillaNSSTrustStoredirectly - e.g. next toPhase4PeppolHttpClientSettings.setSSLContextPeppolMozillaNSS()- change its import fromcom.helger.peppol.securitytocom.helger.nss. The class moved into the new standalone project ph-nss, because the Mozilla NSS root certificates are not Peppol specific and are updated every couple of weeks.phase4-profile-peppoldeclares the dependencycom.helger:ph-nssitself, so nothing needs to be added to your POM, and the API of the class is unchanged -
The Country C1 element of an incoming Peppol SBDH is now always checked, as soon as the SBDH value checks are enabled.
PeppolSBDHDataReader.setCheckForCountryC1(boolean)was removed in peppol-commons 13.0.0, so the deprecated methodsPhase4PeppolDefaultReceiverConfiguration.setCheckSBDHForMandatoryCountryC1(boolean)andPhase4PeppolReceiverConfigurationBuilder.checkSBDHForMandatoryCountryC1(boolean)still compile, but no longer have any effect. A receiver that relied on disabling that check now rejects incoming messages without a Country C1 element. The only remaining way to accept them is to disableperformSBDHValueChecksaltogether, which also disables all other SBDH value checks - Applications that use further parts of peppol-commons directly are affected by more moves and removals than the ones listed here - see the peppol-commons v13.0.0 release notes for the complete list
- If you have a custom subclass of
AS4DefaultPModeResolverthat overrides the protected methodcreateDefaultPMode(String,String,String), move your code to the new 5 argument overloadcreateDefaultPMode(String,String,String,String,String)(since v4.6.1). The 3 argument version still exists and still compiles, but it is deprecated and is no longer called byfindPMode, so an override of it would be silently ignored. Implementations ofIAS4Profileare unaffected - the newcreatePModeTemplateoverload has a default implementation. See #213 - The method
IAS4IncomingAttachmentFactory.createAttachmentnow takes anAS4IncomingMimePartinstead of ajakarta.mail.internet.MimeBodyPartparameter - custom implementations of this interface must be adapted.AS4IncomingMimePartonly parses the header section of a MIME part eagerly, whereas the content stays on the stream and can be consumed exactly once. That is what avoids loading a whole attachment into memory. For the same reason, the methodWSS4JAttachment.createIncomingFileAttachment(MimeBodyPart, AS4ResourceHelper)was deprecated in favour of the new overload taking anAS4IncomingMimePart. See #382 -
The signature of an incoming message must now cover the ebMS
Messagingheader element and all attachments. For messages without attachments, the SOAPBodyelement must be covered as well, as required by the AS4 profile chapter 5.1.4. For messages with attachments it is not required, because chapter 5.1.5 only demands theeb:Messagingheader element and all MIME body parts (since v4.6.1). Since v4.6.1 the required parts are AS4 profile specific: custom profiles can overrideIAS4ProfileValidator.getRequiredSignedParts(boolean)to demand more or less than the generic AS4 rules. Up to v4.5.x it was sufficient that any element was correctly signed for a message to count as "signed" - it was never checked what the signature really covers, which is the base of XML signature wrapping attacks. Messages with an incomplete signature coverage are now rejected withEBMS:0103(Policy Noncompliance). If a partner does not sign all of these parts, the previous behaviour - only logging a warning - can be restored by setting the configuration propertyphase4.incoming.signature.requirefullcoveragetofalse. See #318 and #392 -
Incoming messages are now limited in size.
All limits can be disabled individually by using a value < 0.
See #318
-
phase4.incoming.maxmessagesize.bytes(default 1 GB),phase4.incoming.maxattachmentcount(default 100) andphase4.incoming.attachment.maxsize.bytes(default 1 GB) are answered with HTTP status code 400 (Bad Request, not retryable) -
phase4.incoming.attachment.maxdecompressedsize.bytes(default 1 GB) andphase4.incoming.attachment.maxcompressionratio(default 1000) lead toEBMS:0303(Decompression Failure) in the message processors of the Peppol, DBNAlliance and HR eDelivery profiles - Note the interaction with #382: the previous implicit limit of ~2 GB per MIME part was removed in the same version, so a message larger than 2 GB became receivable and is now limited by configuration instead. Deployments that exchange very large files need to raise or disable these values explicitly
- Custom implementations of
IAS4IncomingMessageProcessorSPIshould translate anIOExceptionof the new typeAS4SizeLimitException, thrown while reading an attachment, intoEBMS:0303- like the shipped profile implementations do
-
- If the configuration property
phase4.http.response.accept.allstatuscodesis disabled, a received SOAP Fault with aPERMANENTdisposition now stops all remaining HTTP retries immediately. Previously all configured retries were exhausted. See #384 - Malformed MIME parts of an incoming message - a header section exceeding the configured maximum size or an unknown
Content-Transfer-Encoding- are now answered with HTTP status code 400 (Bad Request, not retryable) instead of HTTP status code 500. Client side retry logic or monitoring that keys on 5xx status codes is affected. See #382 - Non-parseable bodies of non-success HTTP responses (status code ≥ 300) are now logged on
WARNlevel instead of onERRORlevel, because they typically represent infrastructure error pages (e.g. HTML from a load balancer or reverse proxy) rather than AS4 protocol violations. Log based alerting that triggers onERRORwill no longer fire for these. See #378 -
BasicHttpPostercan optionally reuse HTTP connections across requests via a sharedHttpClientManager(seesetSharedHttpClientManager). This is opt-in, but if it is used, the caller owns the manager and must close it, and the configuredHttpClientFactoryis ignored. See #379
- Peppol SMP certificate revocation checks are enabled by default
- Peppol SMP TLS certificate revocation checks are enabled by default
- Peppol AP TLS certificate revocation checks are enabled by default
-
Peppol TLS connections now rely on the default Java runtime truststore instead of trusting all server certificates.
Three explicit truststore options are available on
Phase4PeppolHttpClientSettings:- Leave the default - JVM
cacertsis used and the configured revocation check mode is applied. - Call
setSSLContextTrustAll()(inherited fromHttpClientSettings) to restore the pre-4.5.0 "trust all server certificates" behaviour. Test environments only. - Call
setSSLContextPeppolMozillaNSS()(since 4.5.1) to use the Mozilla NSS root certificate trust store shipped withpeppol-commons- the strictest production option.
- Leave the default - JVM
- TLS handshake / PKIX failures during message sending are now wrapped with a hint pointing at this page and at the helpers above (since 4.5.1).
- An unfortunate regression requires the
http.proxy.enabled=trueconfiguration property to be enabled. This was relevant only in v4.5.0 and fixed in v4.5.1.
- OSGi bundle support has been removed.
All submodule artifacts are now packaged as plain JAR files instead of OSGi bundles.
If you relied on OSGi metadata (Import-Package, Export-Package, Require-Capability, etc.), you will need to handle this in your OSGi container configuration.
The
Automatic-Module-Namemanifest entry is still present for Java Platform Module System (JPMS) compatibility.
- The SPI interface method
IPhase4PeppolIncomingSBDHandlerSPI.processAS4ResponseMessageis no longer a default method
- The SPI interface for receiving AS4 messages was slightly changed on error handling:
- Instead of
ICommonsList <Ebms3Error>the error list is now of typeAS4ErrorList - Each individual error is now of type
AS4Errorinstead ofEbms3Error - Both API changes should be transparent, so no additional coding is expected besides the type change
- Instead of
- The
@NonNulland@Nullableannotations are now taken from JSpecify and no longer from Jakarta-Annotations
- It requires at least Java 17 for building and execution
- It was updated to the JakartaEE 10 specifications
- Uses Servlet Specification 6.0.x
- The minimum requirements for application servers are
- Tomcat 10.1.x
- Jetty 12.x
- Make sure that all dependent libraries are updated to the required versions (see the release for details)
- All interfaces that declared their methods are throwing
Phase4PeppolExceptionare now throwingPhase4Exceptioninstead - [Peppol] The support for Peppol Policy for use of Identifiers v4.2.0 was dropped
- Make sure that all dependent libraries are updated to the required versions (see the release for details)
- Class
PeppolCACheckerwas replaced with classTrustedCAChecker - The predefined trusted CAs of Peppol were moved from
PeppolCertificateCheckertoPeppolTrustedCA - Enum
EPeppolCertificateCheckResultwas replaced withECertificateCheckResult - Some methods were moved from class
PeppolCertificateHelperto classCertificateHelper - [DBNAlliance] Moved class
Phase4DBNAllianceHttpClientSettingsto modulephase4-profile-dbnalliance - The method
AbstractAS4MessageBuilder.finishFields(and in all derived classes) now have anAS4ResourceHelperparameter
- Due to a change to peppol-commons v10 some classes were moved. See https://github.com/phax/peppol-commons/releases/tag/peppol-commons-parent-pom-10.0.0 for details
-
The
phase4-profile-bpcsubmodule was removed in favour ofphase4-profile-dbnalliance -
The
phase4-spring-boot-demosubmodule was removed in favour of https://github.com/phax/phase4-peppol-standalone -
All deprecated methods marked for removal have been removed
-
Configuration related changes
- The configuration files
private-crypto.propertiesandcrypto.propertiesare deprecated. Please move the properties toapplication.properties, environment variables or Java system properties instead. - The configuration property
phase4.profilewas renamed tophase4.default.profile- the old property is still evaluated as a secondary option - In case you are using
AS4CryptoFactoryPropertiesand- read the configuration from a file: use
AS4CryptoFactoryConfigurationinstead (as a drop-in replacement) - provide the properties manually: use
AS4CryptoFactoryInMemoryKeyStoreinstead (and see below)
- read the configuration from a file: use
- The configuration files
-
Here are the main coding related changes
- Renamed packages
- Renamed package
com.helger.phase4.errortocom.helger.phase4.model.error - Renamed package
com.helger.phase4.httptocom.helger.phase4.messaging.http - Renamed package
com.helger.phase4.messaging.domaintocom.helger.phase4.model.message
- Renamed package
- Moved classes
- Moved classes from
com.helger.phase4.servlettocom.helger.phase4.incoming - Moved classes from
com.helger.phase4.servlet.mgrtocom.helger.phase4.incoming.mgr - Moved classes from
com.helger.phase4.servlet.soaptocom.helger.phase4.incoming.soap - Moved classes from
com.helger.phase4.servlet.spitocom.helger.phase4.incoming.spi - Moved class
AbstractAS4RawResponseConsumertocom.helger.phase4.sender - Moved class
AS4IncomingDumperFileBased.IFileProvidertoIAS4IncomingDumperFileProvider - Moved class
AS4OutgoingDumperFileBased.IFileProvidertoIAS4OutgoingDumperFileProvider - Moved class
AS4RequestHandler.ISoapProcessingFinalizedCallbacktoIAS4SoapProcessingFinalizedCallback - Moved class
ESoapVersionto com.helger.phase4.model` - Moved classes
IAS4IncomingSecurityConfigurationandAS4IncomingSecurityConfigurationtocom.helger.phase4.incoming.crypto - Moved class
IAS4IncomingMessageMetadatatocom.helger.phase4.incoming - Moved class
IAS4RawResponseConsumertocom.helger.phase4.sender - Moved class
IAS4SignalMessageConsumertocom.helger.phase4.incoming - Moved class
IAS4UserMessageConsumertocom.helger.phase4.incoming
- Moved classes from
- Renamed classes
- Renamed class
AbstractAS4UserMessageBuilder.ESimpleUserMessageSendResulttoEAS4UserMessageSendResult - Renamed class
AS4DuplicateManagertoAS4DuplicateManagerXML - Renamed class
AS4MessageStatetoAS4IncomingMessageState - Renamed class
AS4MessagingHelpertoAS4IncomingHelper- And moved to
com.helger.phase4.incoming
- And moved to
- Renamed class
AS4ServletMessageProcessorManagertoAS4IncomingMessageProcessorManager - Renamed class
AS4ServletPullRequestProcessorManagertoAS4IncomingPullRequestProcessorManager - Renamed class
AS4SingleSOAPHeadertoAS4SingleSoapHeader(casing only) - Renamed class
AS4XServletHandler.IHandlerCustomizertoIAS4ServletRequestHandlerCustomizer- Made method
customizeAfterHandlingnon-default
- Made method
- Renamed class
DefaultPModeResolvertoAS4DefaultPModeResolver - Renamed SPI class
IAS4ServletMessageProcessorSPItoIAS4IncomingMessageProcessorSPI- Was also moved to a different package - see above
- Made method
IAS4IncomingMessageProcessorSPInon-default
- Renamed SPI class
IAS4ServletPullRequestProcessorSPItoIAS4IncomingPullRequestProcessorSPI- Was also moved to a different package - see above
- Renamed class
IAS4MessageStatetoIAS4IncomingMessageState - Renamed class
IPModeResolvertoIAS4PModeResolver - Renamed class
ISOAPHeaderElementProcessortoISoapHeaderElementProcessor(casing only) - Renamed class
MimeMessageCreatortoAS4MimeMessageHelper - Renamed class
MPCManagertoMPCManagerXML - Renamed class
Phase4PeppolServletConfigurationtoPhase4PeppolDefaultReceiverConfiguration - Renamed class
Phase4PeppolReceiverCheckDatatoPhase4PeppolReceiverConfiguration - Renamed class
Phase4PeppolSender.BuildertoPhase4PeppolSender.PeppolUserMessageBuilder - Renamed class
Phase4PeppolSender.SBDHBuildertoPhase4PeppolSender.PeppolUserMessageSBDHBuilder - Renamed class
PModeManagertoPModeManagerXML - Renamed class
SoapMimeMultiparttoAS4SoapMimeMultipart - Renamed class
SOAPHeaderElementProcessorExtractEbms3MessagingtoSoapHeaderElementProcessorExtractEbms3Messaging(casing only) - Renamed class
SOAPHeaderElementProcessorRegistrytoSoapHeaderElementProcessorRegistry(casing only) - Renamed class
SOAPHeaderElementProcessorWSS4JtoSoapHeaderElementProcessorWSS4J(casing only)
- Renamed class
- Removed classes
- Removed class
Phase4PeppolClientException
- Removed class
- Altered classes
- Added template parameter to class
AbstractAS4IncomingDumperWithHeaders - Added template parameter to class
AbstractAS4OutgoingDumperWithHeaders - Removed constant
DefaultPModeResolver.DEFAULT_PMODE_RESOLVERin favour of new instances with an AS4 profile ID
- Added template parameter to class
- Moved methods
- Moved methods
AS4ReceiptMessage.getAllDSigReferenceNodesandAS4ReceiptMessage.getAllDSigReferencesto classMessageHelperMethods - Moved all setters from
AS4XServletHandlertoAS4RequestHandler - Moved method
IAS4ResponseAbstraction.wraptoAS4XServletHandler.createResponseAbstraction - Moved methods
MessageHelperMethods.forEachHeaderAndRemoveAfterwardsandMessageHelperMethods.getAndRemoveAllHeadersto classAS4MimeMessageHelper
- Moved methods
- Renamed methods
- Renamed methods
AbstractAS4Client.(get|set)AS4CryptoFactory*to(get|set)CryptoFactory... - Renamed methods
AS4ClientBuiltMessage.getCustomHeaderstogetAllCustomHttpHeaders - Renamed method
AS4ClientErrorMessage.isReceiptShouldBeSignedtoisErrorShouldBeSigned - Renamed method
AS4ClientSentMessage.getResponsetogetResponseContent - Renamed method
AS4ClientSentMessage.hasResponsetohasResponseContent - Renamed method
AS4Configuration.getAS4ProfileIDtogetDefaultAS4ProfileID - Renamed method
AS4ProfileSelector.getAS4ProfileIDtogetDefaultAS4ProfileID - Renamed methods
AS4ProfileSelector.(get|set)CustomAS4ProfileIDto(get|set)CustomDefaultAS4ProfileID - Renamed methods
AS4XServletHandler.(get|set)HandlerCustomizerto(get|set)RequestHandlerCustomizer - Renamed method
IAS4IncomingDumperFileProvider.getFilenametogetDefaultDirectoryAndFilename - Renamed method
IAS4OutgoingDumperFileProvider.getFilenametogetDefaultDirectoryAndFilename - Renamed method
IPModeResolver.getPModeOfIDtofindPMode
- Renamed methods
- Altered methods
- Extended method
AS4DumpReader.decryptAS4Into include an AS4 profile ID parameter - Made method
IAS4CryptoFactory.getKeyPasswordPerAlias(String)non-default - Made method
IAS4IncomingDumper.onEndRequestnon-default and added optional "caughtException" parameter - Made method
IAS4OutgoingDumper.onEndRequestnon-default and added optional "caughtException" parameter - Extended method
IAS4ProfileValidator.validatePModewith an additional parameter to differentiate UserMessage and SignalMessage
- Extended method
- Removed methods
- Removed method
IAS4ProfileManager.hasDefaultProfile - Removed method
IAS4ProfileManager.getDefaultProfileOrNull - Removed method
IAS4ProfileManager.getDefaultProfile - Removed method
IAS4ProfileManager.setDefaultProfileID - Removed method
IAS4ProfileRegistrar.setDefaultProfile
- Removed method
- Renamed packages
-
The most common form of CryptoFactory creation got reworked.
Instead of
final AS4CryptoProperties aCP = new AS4CryptoProperties ().setKeyStoreType (EKeyStoreType.PKCS12)
.setKeyStorePath ("path.p12")
.setKeyStorePassword ("pw")
.setKeyAlias ("cert")
.setKeyPassword ("pw")
.setTrustStoreType (EKeyStoreType.JKS)
.setTrustStorePath ("truststore/complete-truststore.jks")
.setTrustStorePassword ("peppol");
new AS4CryptoFactoryProperties (aCP);use this code now:
new AS4CryptoFactoryInMemoryKeyStore (KeyStoreAndKeyDescriptor.builder ()
.type (EKeyStoreType.PKCS12)
.path ("path.p12")
.password ("pw")
.keyAlias ("cert")
.keyPassword ("pw")
.build (),
TrustStoreDescriptor.builder ()
.type (EKeyStoreType.JKS)
.path ("truststore/complete-truststore.jks")
.password ("peppol")
.build ());In case of problems of PMode resolution, most likely you are missing a default AS4 profiles. Use the following code snippet in your global initialization to pick the correct AS4 profile.
// Example on how to globally use the AS4 Peppol profile
AS4ProfileSelector.setCustomDefaultAS4ProfileID (AS4PeppolProfileRegistarSPI.AS4_PROFILE_ID);The major change in v2.x is the new software baseline.
- It requires at least Java 11 for building and execution
- It was updated to the JakartaEE 9 specifications
- Uses Servlet Specification 5.0.x
- All the namespaces
javax.servletwhere changed tojakarta.servlet - Uses Eclipse Angus instead of Jakarta Mail
- The minimum requirements for application servers are
- Tomcat 10.0.x or Tomcat 10.1.x
- Jetty 11.x
All other potentially breaking changes are listed in News and noteworthy.