MADE Knowledge Base > Automation
Running MADe Headless: Automating the Model Without the GUI
MADe 3.9.1 ships no headless mode. There is no command line switch, no scripting console, and no batch runner. Everything the product does, it does through its window.
It can still be automated, because of what MADe is built on. This article records what was proven by doing it, and separates that from what merely looks possible.
Everything below was exercised against MADe 3.9.1 on Windows in September 2026.
What MADe actually provides
Checked by reading the installation rather than by assumption:
MADE.inicontains no-applicationargument.- Across every
plugin.xmlin<install>/plugins, the only application MADe registers iscom.phm.made.application, which points at the GUI class. Every other application id belongs to Eclipse itself. com.phm.made.reporting.scriptsounds like a scripting engine and is not. It contains 18 classes of BIRT report helpers.
What MADe is built on is a standard Eclipse Equinox OSGi container. -application is an Equinox
launcher argument, not a vendor feature, which means a new application can be added alongside the
GUI one without modifying the product.
The approach
Add a small OSGi bundle that declares its own application, register it by editing a copy of the installation's configuration, and launch the Equinox launcher directly. The vendor installation is never modified.
"<install>\jre\bin\java.exe" -jar "<install>\plugins\org.eclipse.equinox.launcher_*.jar" ^
-application <your.application.id> ^
-configuration <copy of configuration> -data <workspace> -consoleLog -nosplash
A useful first step, before writing any code: launch with a deliberately wrong -application id.
The framework starts, lists every application it knows about, and exits. That confirms MADe's
runtime starts perfectly well with no window.
Licensing is not an obstacle. The com.phm.made bundle activates in about 6.5 seconds with no
error and no licence complaint.
What works
Proven, with measurements:
| Capability | Result |
|---|---|
| Start the MADe runtime with no window | Yes |
Open a .made project |
Yes, 0.6 to 1.2 seconds |
| Read the item tree | Yes |
| Read reliability and criticality per item | Failure rate, MTTR, distribution, LRU, criticality flags |
| Change a value and persist it | Yes, verified by reopening and by comparing the archive |
| Run MADe's own API export operation | Yes, all categories, against a live endpoint |
| Run MADe's own API import operation | Partly, see the limitation at the end |
Five things that will cost you a day each
1. MADe exports almost nothing, so normal OSGi wiring does not reach it
com.phm.made exports 5 packages, none of the useful ones. com.phm.made.api exports none at all.
Require-Bundle and Import-Package cannot see ProjectManager or MADeProjectService.
Use Bundle.loadClass, which resolves against the bundle's own class loader and is not restricted by
Export-Package.
2. "The project is no longer compatible with this version of MADE"
Opening any project fails with IncompatibleProjectTypeException and a message telling you to
convert the project to an archive, on a file that is already a perfectly good archive.
The message names the wrong cause. MADe stores projects as TrueVFS archives, and the .made
extension is bound to an archive driver in exactly one place: the constructor of
TrueZIPIOAccessManager. In the GUI that happens on the way in. Construct one before opening a
project, on the same thread, because TrueVFS configuration is per thread.
3. Headless means no window, not no display
ProjectManager.doOpenProject() finishes by calling updateShellText(), which is a
Display.syncExec. There is no flag to skip it, and the same call appears in close, new project and
save-as. With no event loop, the call never returns and your automation hangs with no error at all.
The fix is not to remove the UI, it is to give MADe the contract it expects: create an SWT Display
on the main thread, pump readAndDispatch, and run every MADe call on that thread. No window is ever
created.
Once a display is pumping, the same call throws IllegalStateException: Workbench has not been created yet after the work has already succeeded. Treat that as a cosmetic tail, not as the
outcome: ask getCurrentProjectOptional() what actually happened.
4. A save can report success and write nothing
Setting a value and saving is not enough. Four things are required, and missing any one produces a clean success with an unchanged file:
- set the value,
- call
setDirty(true)on the element, because a setter does not mark it, - write the element through
ServiceManager.getIOAccessManager().saveElement(element, path), - call
syncProject()and then close the project, which is what rewrites the archive.
The path in step 3 cannot simply be read off the element. getRelativeFilePath() is built from
getFolderPath(), which walks up through getParent(), and headless every child component reports a
null parent. The path collapses to the archive root and the element is written to the wrong entry.
Derive it from the system that owns the component instead.
Also worth knowing: MADe's own project save drops externalApplicationData.dat, the record of which
external application the project came from. If your project is linked to Teamcenter or another
system, prefer writing the individual elements.
5. Loading the taxonomy is a separate step
The GUI splash screen says "Loading Taxonomy" for a reason. Nothing in bundle activation does it, and
ServiceManager.getTaxonomyManager().load() has to be called explicitly. It takes about a second.
Skip it and most things still work, which is what makes this one dangerous. Items, RAMS and criticality all export perfectly, and functions alone fail, because a function resolves its own name through the taxonomy. The failure surfaces only as "an unknown internal error occurred in MADE".
Diagnosing "an unknown internal error occurred in MADE"
That message is what MADe shows for almost any internal failure, in the GUI as well as headless, and it never names a cause.
The cause is written to the Eclipse workspace log:
%USERPROFILE%\MADE\<workspace>\.metadata\.log
Every failure met while doing this work was named precisely in that file, usually as a
NullPointerException with the exact class and method. If a MADe wizard fails and the dialog tells
you nothing, read that log before anything else.
Driving the API integration
MADe's Integration menu connects out to an API endpoint that a system integrator implements. Those operations can be run from code, which means an unattended process can push a model out of MADe on a schedule.
An export is assembled as a list of per-category jobs wrapped in an operation:
- every export job takes
(host address, API version, request method, filter, input object), RequestMethod.PUTis the wizard's Snapshot andPATCHis its Delta,- the operation is an Eclipse
ICoreRunnable, so running it is a singlerun(monitor)call.
Two details that will bite: the jobs take the host address while the operation takes the host
name, both typed String, so a mix-up compiles and then fails inside the HTTP client; and
ApiHostsManager.readHosts() must be called first or a configured host will not be found.
A run of items, RAMS, criticality and functions completed in 1.6 seconds and delivered four payloads to the endpoint.
The current limitation: importing updates
The import direction runs, and stops one step short.
Given data on the endpoint, MADe fetches it, computes a diff correctly (it identifies the right component and marks exactly the right fields as updated), the changes can be approved programmatically, and the commit reports success and marks the changes as consumed.
The model values do not change. Comparing the project archive before and after shows the affected elements are byte for byte identical.
Creating a new project by import is a different path and is known to work: it is how a model can be loaded into MADe from an external system in the first place. What has not been made to work headless is applying an import as an update to items that already exist.
If you are attempting this, note that a change in MADe's diff model has a staging lifecycle
(isPending, isStaged, isApproved, isRejected) and only approved changes are committed. That
part is necessary and is not sufficient.
A word on verification
Three separate times during this work, MADe reported success for an operation that had done nothing: a save that wrote no bytes, a "wrote" flag on an archive that had not changed, and an import that locked its changes without applying them.
None of them were visible in the response. All three were caught the same way: reopen the project in
a fresh process, or compare the entries of the .made archive before and after.
If you automate MADe, check the artifact, not the return value.
Source: Original research, Siemens Xcelerator workspace. Not a Siemens Support Center article. · retrieved 2026-09-09