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 ¶
- func Export(w io.Writer, ch *Channel) error
- type Bundle
- type Channel
- type ChannelGroup
- type ChannelProperties
- type CodeTemplate
- type CodeTemplateLibrary
- type Connector
- type ConnectorMode
- type ExportResult
- type Filter
- type JavaReport
- type JavaUse
- type Portability
- type Rule
- type RuleKind
- type Step
- type StepKind
- type Transformer
- type Verdict
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Export ¶
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
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
ParseBundleFile reads any Mirth-family export from disk.
func (*Bundle) ChannelNames ¶ added in v0.1.5
ChannelNames maps every channel id in the bundle to its name.
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 ¶
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 ¶
ParseChannelFile reads a Mirth channel export from disk.
func (*Channel) AllConnectors ¶
AllConnectors returns the source followed by every destination.
func (*Channel) HasJavaScript ¶
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 ¶
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
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 ¶
SetFilterXML attaches a filter document. See SetTransformerXML.
func (*Connector) SetPropertiesXML ¶
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 ¶
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 ¶
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 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 Transformer ¶
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" )