mirth

package
v0.1.23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Java analysis for imported Mirth channels.

Why this exists

Mirth runs Rhino on the JVM, so a Mirth script can call any Java on the classpath, including Mirth's own server classes. Perfuse runs goja and has no Java runtime. It refuses those calls with an error naming the class - but that refusal happens when the script runs, which for a source connector means when the first real message arrives.

So a migration used to go: the import succeeds silently, the channel deploys, and the first message off the wire fails. A migration that looks clean and breaks on first traffic is worse than one that refuses at the door, because the person who did it has already told their colleagues it worked.

This finds those calls at import time and says what to do about each one.

Why four verdicts and not two

"Uses Java" is one bit of information and the wrong one, because the Java a real Mirth channel uses falls into groups with completely different amounts of work attached. Roughly half of what sites actually write is SimpleDateFormat and HashMap, which JavaScript does natively. Some of it is Mirth's documented script API, which Perfuse already implements under the same names. A little of it wants to start and stop channels, which Perfuse can do over its REST API but does not expose to scripts. And some of it is a vendor jar that is never going to run here.

Reporting those as one number tells a migrator nothing about whether their afternoon or their quarter is at stake.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Export

func Export(w io.Writer, ch *Channel) error

Export writes a Mirth channel XML file from a Channel struct.

This is the reverse of ParseChannel: it produces XML that Mirth Connect (4.x) and Open Integration Engine can import. The output includes the Java class attributes that Mirth requires on properties elements — without those, the import silently fails or rejects the file.

Limitations:

  • Only connectors whose PropertiesClass we recognise get full property export. Unknown connector types get their properties map written as flat elements, which Mirth may or may not accept depending on the plugin.
  • Iterator steps and rules are exported but complex nesting may not survive round-trip through all Mirth versions.

The output targets Mirth 4.5.x / OIE 4.6.x format.

Types

type Bundle added in v0.1.5

type Bundle struct {
	// Kind names the document: "channel", "channel list", "server backup", "channel group" or "code template libraries".
	Kind string

	// Version is the engine version on the root element, when there is one.
	Version string

	Channels  []*Channel
	Groups    []ChannelGroup
	Libraries []CodeTemplateLibrary

	// GlobalScripts are the server-wide deploy, undeploy, preprocessor and postprocessor scripts, by name. Only a backup has them.
	GlobalScripts map[string]string
}

Bundle is everything one document carries.

func ParseBundle added in v0.1.5

func ParseBundle(r io.Reader) (*Bundle, error)

ParseBundle reads any of the documents a Mirth-family engine exports: a channel, a list of channels, a server backup, a channel group or a code template library export.

func ParseBundleFile added in v0.1.5

func ParseBundleFile(path string) (*Bundle, error)

ParseBundleFile reads any Mirth-family export from disk.

func (*Bundle) ChannelNames added in v0.1.5

func (b *Bundle) ChannelNames() map[string]string

ChannelNames maps every channel id in the bundle to its name.

func (*Bundle) GroupOf added in v0.1.5

func (b *Bundle) GroupOf(channelID string) string

GroupOf names the group a channel belongs to, or "".

func (*Bundle) LibrariesFor added in v0.1.5

func (b *Bundle) LibrariesFor(channelID string) []CodeTemplateLibrary

LibrariesFor returns the libraries a channel could call into, by name order.

type Channel

type Channel struct {
	ID          string
	Name        string
	Description string
	Enabled     bool
	Revision    int

	Source       Connector
	Destinations []Connector

	PreprocessingScript  string
	PostprocessingScript string
	DeployScript         string
	UndeployScript       string

	Properties ChannelProperties

	// Unrecognised holds element names we saw but did not map.
	Unrecognised []string

	// MirthVersion is the version attribute on the root element, if present.
	MirthVersion string
	// contains filtered or unexported fields
}

Channel is one imported Mirth channel.

func ParseChannel

func ParseChannel(r io.Reader) (*Channel, error)

ParseChannel reads a Mirth channel export.

It does not fail on constructs it does not know. Unrecognised elements are collected on the Channel and Connector so callers can report them.

func ParseChannelFile

func ParseChannelFile(path string) (*Channel, error)

ParseChannelFile reads a Mirth channel export from disk.

func (*Channel) AllConnectors

func (c *Channel) AllConnectors() []Connector

AllConnectors returns the source followed by every destination.

func (*Channel) HasJavaScript

func (c *Channel) HasJavaScript() bool

HasJavaScript reports whether the channel contains any JavaScript at all. A channel with none can be translated mechanically; one with any needs the JavaScript engine, so this single answer decides how a migration goes.

func (*Channel) ScanJava

func (c *Channel) ScanJava() *JavaReport

ScanJava analyses every script in a channel.

func (*Channel) SetPropertiesXML

func (c *Channel) SetPropertiesXML(r io.Reader) error

SetPropertiesXML attaches the channel-level properties document.

Mirth's ChannelProperties serialises as <channelProperties>, and inside a channel the same block is <properties> with no class attribute. The elements matter more than they look: a channel missing metaDataColumns, attachmentProperties or initialState is one Mirth discards, and those are exactly the ones a reconstruction from a model leaves out.

type ChannelGroup added in v0.1.5

type ChannelGroup struct {
	ID          string
	Name        string
	Description string
	ChannelIDs  []string
}

ChannelGroup is a named set of channels.

type ChannelProperties

type ChannelProperties struct {
	MessageStorageMode      string
	EncryptData             bool
	RemoveContentOnComplete bool
	StoreAttachments        bool
	PruneMetaDataDays       int
	PruneContentDays        int
	ClearGlobalChannelMap   bool
	ResourceIDs             []string
}

ChannelProperties covers the channel-level settings that change behaviour we have to reproduce, notably message storage, which drives disk usage.

type CodeTemplate added in v0.1.5

type CodeTemplate struct {
	ID   string
	Name string
	// Type is FUNCTION, DRAG_AND_DROP_CODE or COMPILED_CODE. Only FUNCTION and COMPILED_CODE are loaded into a script's scope; a
	// drag-and-drop snippet is pasted into a step by hand in the Administrator, so it is not code that runs by itself.
	Type string
	Code string
	// Contexts are where Mirth makes the template available, such as SOURCE_FILTER_TRANSFORMER.
	Contexts []string
}

CodeTemplate is one shared script.

func (CodeTemplate) Loaded added in v0.1.5

func (t CodeTemplate) Loaded() bool

Loaded reports whether Mirth puts the template into a script's scope. A drag-and-drop snippet is only ever copied into a step.

type CodeTemplateLibrary added in v0.1.5

type CodeTemplateLibrary struct {
	ID          string
	Name        string
	Description string

	// IncludeNewChannels makes the library visible to every channel not explicitly disabled. Mirth's default for a new library is false.
	IncludeNewChannels bool
	EnabledChannelIDs  []string
	DisabledChannelIDs []string

	Templates []CodeTemplate
}

CodeTemplateLibrary is a set of shared scripts and the channels that can see them.

func (CodeTemplateLibrary) AppliesTo added in v0.1.5

func (l CodeTemplateLibrary) AppliesTo(channelID string) bool

AppliesTo reports whether a channel's scripts could call into the library.

func (CodeTemplateLibrary) Source added in v0.1.5

func (l CodeTemplateLibrary) Source() string

Source is the library as one script file: every loaded template, in order, each headed by a comment naming it.

type Connector

type Connector struct {
	MetaDataID int
	Name       string
	Mode       ConnectorMode
	Enabled    bool

	// Transport is Mirth's transportName, e.g. "TCP Listener", "HTTP Sender".
	Transport string

	// PropertiesClass is the concrete Java properties class, which identifies
	// the connector far more reliably than the display name.
	PropertiesClass string

	// Properties is the transport's settings flattened to dotted paths.
	Properties map[string]string

	WaitForPrevious bool

	Filter       Filter
	Transformer  Transformer
	Unrecognised []string
	// contains filtered or unexported fields
}

Connector is a source or destination: a transport plus its filter and transformer.

func (*Connector) SetFilterXML

func (c *Connector) SetFilterXML(r io.Reader) error

SetFilterXML attaches a filter document. See SetTransformerXML.

func (*Connector) SetPropertiesXML

func (c *Connector) SetPropertiesXML(r io.Reader) error

SetPropertiesXML attaches a properties document to the connector.

The document is what XStream writes for a bare properties object, whose root element is the fully qualified class name. Inside a channel the same block appears as <properties class="..."> with the class as an attribute, so the root is renamed and the class moved.

func (*Connector) SetRawProperty

func (c *Connector) SetRawProperty(path, value string) error

SetRawProperty sets a leaf inside the attached properties document, addressed by dotted path.

It is an error for the path not to exist. That is the point: Mirth's own default document lists every element the connector has, so a path that is not there is a path Mirth does not know, and writing it anyway produces a channel the server throws away while reporting nothing useful. Failing here names the path instead.

func (*Connector) SetTransformerXML

func (c *Connector) SetTransformerXML(r io.Reader) error

SetTransformerXML attaches a transformer document to the connector, and SetFilterXML a filter.

Both come from Mirth's own serialisation of an empty Transformer and Filter. Writing them from this side added inboundDataType and outboundDataType elements that Mirth's own output does not have.

type ConnectorMode

type ConnectorMode string

ConnectorMode distinguishes the single source from the destinations.

const (
	ModeSource      ConnectorMode = "SOURCE"
	ModeDestination ConnectorMode = "DESTINATION"
)

type ExportResult

type ExportResult struct {
	// Warnings lists elements or features that exist on the Channel struct but
	// could not be represented in Mirth XML. A non-empty list means the export
	// is lossy: something will be lost if this file is imported into Mirth.
	Warnings []string
}

ExportResult reports what happened during an export.

func ExportWithWarnings

func ExportWithWarnings(w io.Writer, ch *Channel) (ExportResult, error)

ExportWithWarnings is like Export but also returns an ExportResult reporting any lossy conversion: features the channel has that Mirth XML cannot express. A caller that cares about silent data loss (which should be all of them) should use this rather than Export.

type Filter

type Filter struct {
	Rules []Rule
}

Filter is the accept/reject logic in front of a transformer.

type JavaReport

type JavaReport struct {
	Uses []JavaUse

	// Counts by verdict, so a caller does not have to tally them to decide what to show.
	Supported    int
	Rewritable   int
	NeedsFeature int
	OutOfScope   int

	// ExternalScripts holds paths from ExternalScript steps.
	//
	// Separate from Uses because the script is not in the export - only a path on the old server's filesystem - so
	// nothing can be said about its contents. Reporting it as "no Java found" would be a lie by omission, and this is
	// the one finding where the honest answer is that the file has to be fetched and looked at.
	ExternalScripts []string
}

JavaReport is everything found in one channel.

func (*JavaReport) Blocked

func (r *JavaReport) Blocked() bool

Blocked reports whether anything found will not work after migration.

func (*JavaReport) Portability

func (r *JavaReport) Portability() Portability

Portability reads the scan as a lock-in audit.

func (*JavaReport) Summary

func (r *JavaReport) Summary() string

Summary is one line for a migrator, naming counts rather than a proportion.

Counts because "83% translatable" is not a decision. Seventeen calls to a vendor jar is.

type JavaUse

type JavaUse struct {
	// Reference is what the script wrote, near enough to find with a search.
	Reference string

	// Where names the script, in the words the Mirth administrator interface uses.
	Where string

	// Line is the line within that script, counting from one.
	Line int

	// Verdict says what can be done.
	Verdict Verdict

	// Advice says what to do, naming the replacement where there is one.
	Advice string
}

JavaUse is one Java reference found in a channel's scripts.

type Portability

type Portability struct {
	// Total is every Java reference found, including the harmless ones.
	//
	// Reported prominently and immediately qualified. It is the number that looks alarming and is not the finding, and leaving it
	// out would be its own dishonesty - somebody who greps their own exports will find it and wonder what else was shaped.
	Total int `json:"total"`

	// Portable is references that run anywhere: already provided, or ordinary Java with a direct equivalent.
	Portable int `json:"portable"`

	// VendorOnly is the count that constitutes lock-in - calls that need the vendor's own server or a vendor jar.
	VendorOnly int `json:"vendorOnly"`

	// NeedsAnotherRoute is capability that exists outside a script: a channel started through an API rather than in JavaScript.
	//
	// Separate from VendorOnly because the difference is "this is impossible" against "this is a REST call away", and collapsing
	// them would overstate the finding in the direction that flatters the argument.
	NeedsAnotherRoute int `json:"needsAnotherRoute"`

	// Unknown is external scripts, whose contents are not in the export.
	//
	// Counted rather than assumed either way. A path on the old server's filesystem could hold anything, and both "assume it is
	// fine" and "assume it is lock-in" are guesses presented as findings.
	Unknown int `json:"unknown"`

	// VendorReferences are the specific calls behind VendorOnly, deduplicated and sorted.
	//
	// Named because a count is an assertion and a list is evidence. Anybody being shown this should be able to open the channel and
	// look at the line.
	VendorReferences []string `json:"vendorReferences"`
}

Portability is what a site can act on: how much of its integration logic runs only on one vendor's software.

func (Portability) Verdict

func (p Portability) Verdict() string

Verdict states what the audit found, in the words somebody would use to decide.

Written to be quotable without a caveat attached, because a sentence that needs one will be quoted without it.

type Rule

type Rule struct {
	Sequence int
	Name     string
	Kind     RuleKind
	RawKind  string

	// Operator is how this rule combines with the previous one: NONE, AND, OR.
	Operator string

	// Field, Condition and Values describe a RuleBuilder rule.
	Field     string
	Condition string
	Values    []string

	// Script holds JavaScript for JavaScript rules.
	Script string

	Children []Rule
}

Rule is one filter rule.

type RuleKind

type RuleKind string

RuleKind is the normalised kind of a filter rule.

const (
	RuleBuilder        RuleKind = "RuleBuilder"
	RuleJavaScript     RuleKind = "JavaScript"
	RuleExternalScript RuleKind = "ExternalScript"
	RuleIterator       RuleKind = "Iterator"
	RuleUnknown        RuleKind = "Unknown"
)

type Step

type Step struct {
	Sequence int
	Name     string
	Kind     StepKind

	// RawKind is the original XML element name, kept so an unrecognised step
	// can still be reported precisely.
	RawKind string

	// Variable and Mapping are used by Mapper steps.
	Variable string
	Mapping  string
	// Scope and DefaultValue are a Mapper step's: which map the variable goes into (CHANNEL, CONNECTOR, GLOBAL_CHANNEL, GLOBAL,
	// RESPONSE) and what it holds when the expression is empty or throws.
	Scope        string
	DefaultValue string

	// Script holds JavaScript for JavaScript steps, the stylesheet for XSLT
	// steps, or the path for ExternalScript steps.
	Script string

	// Children are nested steps, used by Iterator.
	Children []Step
}

Step is one transformer step. The fields are a union across step kinds; which ones are populated depends on Kind.

type StepKind

type StepKind string

StepKind is the normalised kind of a transformer step.

const (
	StepMapper         StepKind = "Mapper"
	StepJavaScript     StepKind = "JavaScript"
	StepMessageBuilder StepKind = "MessageBuilder"
	StepXSLT           StepKind = "XSLT"
	StepExternalScript StepKind = "ExternalScript"
	StepIterator       StepKind = "Iterator"
	StepUnknown        StepKind = "Unknown"
)

type Transformer

type Transformer struct {
	InboundDataType  string
	OutboundDataType string
	Steps            []Step
}

Transformer is an ordered list of steps plus the datatypes either side.

type Verdict

type Verdict string

Verdict says what can be done about one Java reference.

const (
	// VerdictSupported means Perfuse provides this already, under this name or a named alternative.
	VerdictSupported Verdict = "supported"

	// VerdictRewritable means it is ordinary Java that JavaScript does natively.
	//
	// Distinguished from VerdictSupported because somebody has to edit the script. It is minutes of work per site
	// rather than none, and a migrator planning a cutover needs to know which.
	VerdictRewritable Verdict = "rewritable"

	// VerdictNeedsFeature means Perfuse can do this but not from a script.
	//
	// Kept separate from VerdictOutOfScope, which it would otherwise be lumped into, because the distinction is the
	// difference between "this is impossible" and "this is a REST call away". Channel lifecycle is the case that
	// matters: POST /api/channels/{name}/start exists and does exactly what ChannelUtil.startChannel does.
	VerdictNeedsFeature Verdict = "needs-feature"

	// VerdictOutOfScope means it needs a JVM or Mirth's own server, and will not work here.
	//
	// Stated plainly rather than softened. A site with one of these has a decision to make, and the useful thing is
	// for them to make it now rather than discover it during a cutover.
	VerdictOutOfScope Verdict = "out-of-scope"
)

Directories

Path Synopsis
Package mirthlive finds the running Mirth-family engines that the interoperability tests talk to.
Package mirthlive finds the running Mirth-family engines that the interoperability tests talk to.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL