001package org.andromda.maven.plugin;
002
003import java.io.File;
004import java.io.InputStream;
005import java.net.MalformedURLException;
006import java.net.URL;
007import java.text.Format;
008import java.text.SimpleDateFormat;
009import java.util.Date;
010import org.andromda.core.common.ResourceUtils;
011import org.andromda.core.configuration.Configuration;
012import org.andromda.core.configuration.Model;
013import org.andromda.core.configuration.Repository;
014import org.apache.commons.lang.StringUtils;
015import org.apache.maven.plugin.MojoExecutionException;
016
017/**
018 * Exports the MagicDraw project file to EMF XMI
019 * (requires valid MagicDraw installation in MD_HOME, but
020 * only if target files are not up-to-date)
021 *
022 * @goal export2emf
023 * @phase generate-sources
024 * @author Jens Vagts
025 */
026public class MagicDrawExportEMFXMIMojo
027    extends AbstractAndroMDAMojo
028{
029    /**
030     * Name of the environment variable pointing to the MagicDraw home directory
031     */
032    private final String MD_HOME = "MD_HOME";
033
034    /**
035     * The home/root directory of the magicdraw installation.
036     *
037     * @parameter expression="${magicDrawHome}"
038     */
039    private String magicDrawHome;
040
041    /**
042     * @see org.andromda.maven.plugin.AbstractAndroMDAMojo#execute(org.andromda.core.configuration.Configuration)
043     */
044    public void execute(final Configuration configuration)
045        throws MojoExecutionException
046    {
047        try
048        {
049            //export each file (uri) of each model in each repository
050            final Repository[] repositories = configuration.getRepositories();
051            if (repositories == null || repositories.length == 0) {
052                getLog().info("No repositories for export in configuration defined.");
053                return;
054            }
055            int repositoryCount = repositories.length;
056            for (int ctr = 0; ctr < repositoryCount; ctr++)
057            {
058                final Repository repository = repositories[ctr];
059                if (repository != null)
060                {
061                    final Model[] models = repository.getModels();
062                    final int modelCount = models.length;
063                    for (int ctr2 = 0; ctr2 < modelCount; ctr2++)
064                    {
065                        final Model model = models[ctr2];
066                        if ("emf-uml2".equals(model.getType()))
067                        {
068                            String[] uris = model.getUris();
069                            for (int u = 0; u < uris.length; u++)
070                            {
071                                exportFile(uris[u]);
072                            }
073                        }
074                    }
075                }
076            }
077
078        }
079        catch (Throwable throwable)
080        {
081            throw new MojoExecutionException("Error exporting MagicDraw project file to EMF XMI", throwable);
082        }
083    }
084
085    private void exportFile(String dest) throws Exception {
086        final String UML2EXT = ".uml2";
087        final String MDEXT1 = ".xml.zip";
088        final String MDEXT2 = ".mdzip";
089
090        //get the source file name from the destination name (we expect xml.zip)
091        if (!dest.endsWith(UML2EXT))
092        {
093            getLog().warn("Ignoring model file " + dest + ", since it seems not to be of type 'uml2'");
094            return;
095        }
096
097        //check for first MD extension
098        //(use URL.getFile() to get rid of spaces in file name)
099        String source = StringUtils.replace(dest, UML2EXT, MDEXT1);
100        URL sourceUrl = null;
101        try {
102            sourceUrl = new URL(ResourceUtils.normalizePath(source));
103        } catch (MalformedURLException e) {
104            throw new MojoExecutionException("Invalid source model file name [" + source + "]: " + e.getMessage());
105        }
106
107        //check for for second MD extension
108        File sourceFile = new File(sourceUrl.getFile());
109        if (!sourceFile.exists())
110        {
111            source = StringUtils.replace(dest, UML2EXT, MDEXT2);
112            try {
113                sourceUrl = new URL(ResourceUtils.normalizePath(source));
114            } catch (MalformedURLException e) {
115                throw new MojoExecutionException("Invalid source model file name [" + source + "]: " + e.getMessage());
116            }
117        }
118        sourceFile = new File(sourceUrl.getFile());
119        if (!sourceFile.exists())
120        {
121            throw new MojoExecutionException("Model file [" + source + "] does not exist");
122        }
123
124        //check for destination (emf) file
125        URL destUrl = null;
126        try {
127            destUrl = new URL(ResourceUtils.normalizePath(dest));
128        } catch (MalformedURLException e) {
129            throw new MojoExecutionException("Invalid destination model file name [" + dest + "]: " + e.getMessage());
130        }
131
132        File destFile = new File(destUrl.getFile());
133        if (!destFile.exists())
134        {
135            getLog().debug("No old model file [" + dest + "] existing");
136        }
137        else
138        {
139            if (getLog().isDebugEnabled())
140            {
141                Format formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
142                getLog().debug("- MagicDraw model file ["+sourceFile.getName()+"] date = " + formatter.format(new Date(sourceFile.lastModified())));
143                getLog().debug("- EMF model file ["+destFile.getName()+"] date = " + formatter.format(new Date(destFile.lastModified())));
144            }
145            if (destFile.lastModified() >= sourceFile.lastModified())
146            {
147                getLog().info("Model file [" + dest + "] is up-to-date");
148                return;
149            }
150        }
151
152        //check for valid magic draw installation
153        checkForMagicDraw();
154
155        //perform the export via MagicDraw
156        getLog().info("Exporting model file [" + source + "] ...");
157        String command = '\"' + exporterPath + '\"'
158                + " project_file=\"" + sourceFile.getPath() + '\"'
159                + " destination_dir=\"" + sourceFile.getParent() + '\"'
160                + " load_all_modules=true";
161        Process process = Runtime.getRuntime().exec(command);
162
163        //since at least the windows version forks the magicdraw process,
164        //we have to synchronize via input stream reading
165        InputStream is = process.getInputStream();
166        final byte[] buf = new byte[128];
167        int length;
168        while ((length = is.read(buf)) > 0)
169        {
170            getLog().info(new String(buf, 0, length));
171        }
172        process.waitFor();
173        process.destroy();
174        int err = process.exitValue();
175        if (err != 0)
176        {
177            throw new MojoExecutionException("MagicDraw export returned error code " + err);
178        }
179        getLog().info("Successfully exported model file.");
180    }
181
182    /**
183     * only check once for magic draw installation
184     */
185    private boolean checkedMagicDraw = false;
186
187    /**
188     * The export executable file extension (.exe for Windows, nothing for *ix)
189     */
190    private String exportExt = "";
191
192    /**
193     * The full path name to the exporter plugin executable
194     */
195    private String exporterPath;
196
197    private void checkForMagicDraw() throws MojoExecutionException
198    {
199        if (!checkedMagicDraw)
200        {
201            if (magicDrawHome == null)
202            {
203                magicDrawHome = System.getenv(MD_HOME);
204            }
205
206            if (magicDrawHome == null)
207            {
208                throw new MojoExecutionException("MagicDraw home directory not defined: please define either a configuration variable \"magicDrawHome\" in your pom or the environment variable \""+ MD_HOME + "\"!");
209            }
210
211            File home = new File(magicDrawHome);
212            if (!home.exists())
213            {
214                throw new MojoExecutionException("Invalid MagicDraw home directory specified: " + magicDrawHome);
215            }
216
217            //check for running os
218            String os = System.getProperty("os.name");
219            if (os.contains("Windows"))
220            {
221                exportExt = ".exe";
222            }
223
224            //check for plugin name (has changed from MD 11.5 to 11.6)
225            String pluginName115 = "com.nomagic.magicdraw.emfuml2export";
226            String pluginName116 = "com.nomagic.magicdraw.emfuml2xmi";
227            String pluginName15 = "com.nomagic.magicdraw.emfuml2xmi_v1";
228            exporterPath = magicDrawHome
229            + File.separator + "plugins"
230            + File.separator + pluginName15
231            + File.separator + "exportEMFXMI" + exportExt;
232            File exporter = new File(exporterPath);
233            if (!exporter.exists())
234            {
235                exporterPath = magicDrawHome
236                    + File.separator + "plugins"
237                    + File.separator + pluginName116
238                    + File.separator + "exportEMFXMI" + exportExt;
239                exporter = new File(exporterPath);
240                if (!exporter.exists())
241                {
242                    exporterPath = magicDrawHome
243                    + File.separator + "plugins"
244                    + File.separator + pluginName115
245                    + File.separator + "exportEMFXMI" + exportExt;
246                }
247            }
248
249            exporter = new File(exporterPath);
250            if (!exporter.exists())
251            {
252                throw new MojoExecutionException("No exporter plugin found in MagicDraw home directory " + magicDrawHome);
253            }
254
255            checkedMagicDraw = true;
256        }
257    }
258}