source: trunk/org.modelica.mdt.omc/src/org/modelica/mdt/omc/OMCProxy.java @ 239

Last change on this file since 239 was 239, checked in by boris, 19 years ago
  • moved out code that handles communication with OMC to a separate plugin
File size: 19.7 KB
Line 
1/*
2 * This file is part of Modelica Development Tooling.
3 *
4 * Copyright (c) 2005, Link�pings universitet, Department of
5 * Computer and Information Science, PELAB
6 *
7 * All rights reserved.
8 *
9 * (The new BSD license, see also
10 * http://www.opensource.org/licenses/bsd-license.php)
11 *
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are
15 * met:
16 *
17 * * Redistributions of source code must retain the above copyright
18 *   notice, this list of conditions and the following disclaimer.
19 *
20 * * Redistributions in binary form must reproduce the above copyright
21 *   notice, this list of conditions and the following disclaimer in
22 *   the documentation and/or other materials provided with the
23 *   distribution.
24 *
25 * * Neither the name of Link�pings universitet nor the names of its
26 *   contributors may be used to endorse or promote products derived from
27 *   this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42package org.modelica.mdt.omc;
43
44import java.io.BufferedReader;
45import java.io.File;
46import java.io.FileReader;
47import java.io.IOException;
48import java.util.StringTokenizer;
49import java.util.Vector;
50
51import org.eclipse.core.resources.IFile;
52import org.eclipse.core.runtime.Platform;
53import org.modelica.mdt.ErrorManager;
54import org.modelica.mdt.compiler.ConnectException;
55import org.modelica.mdt.compiler.IModelicaCompiler;
56import org.modelica.mdt.compiler.InvocationError;
57import org.modelica.mdt.compiler.ModelicaParser;
58import org.modelica.mdt.compiler.UnexpectedReplyException;
59import org.modelica.mdt.core.IModelicaClass;
60import org.modelica.mdt.omc.internal.ElementLocation;
61import org.modelica.mdt.omc.internal.ParseResults;
62import org.modelica.mdt.omc.internal.OMCParser;
63import org.modelica.omc.internal.corba.OmcCommunication;
64import org.modelica.omc.internal.corba.OmcCommunicationHelper;
65import org.omg.CORBA.ORB;
66
67/**
68 * The OMCProxy is the glue between the OpenModelica Compiler and MDT.
69 * It uses the interactive API of OMC to get information about classes
70 * and to load classes into OMC.
71 *
72 * @author Andreas Remar
73 */
74public class OMCProxy implements IModelicaCompiler
75{
76    /* the CORBA object */
77    private static OmcCommunication omcc;
78   
79    /* what Operating System we're running on */
80    private static String os;
81   
82    /* indicates if we've setup the communication with OMC */
83    private boolean hasInitialized = false;
84   
85    /* indicates if the Modelica System Library has been loaded */
86    private boolean systemLibraryLoaded = false;
87   
88    /* should we trace the calls to sendExpression? */
89    private static boolean traceOMCCalls = false;
90    private static boolean traceOMCStatus = false;
91    static
92    {
93        /* load debug options and set debug flag variables accordingly */
94       
95       
96        String value = Platform.getDebugOption  /*load trace/omcCalls flag */
97            ("org.modelica.mdt.omc/trace/omcCalls");
98        if (value != null && value.equalsIgnoreCase("true"))
99        {
100            traceOMCCalls = true;
101        }
102       
103        value = Platform.getDebugOption
104        ("org.modelica.mdt.omc/trace/omcStatus");
105        if (value != null && value.equalsIgnoreCase("true"))
106        {
107            traceOMCStatus = true;
108        }
109    }
110   
111    public OMCProxy()
112    {
113       
114    }
115
116    /**
117     * Reads in the OMC CORBA object reference from a file on disk.
118     * @return the object reference as a <code>String</code>
119     */
120    private static String readObjectFromFile() throws ConnectException
121    {
122        File f = new File(getPathToObject());
123        String stringifiedObjectReference = null;
124
125        BufferedReader br = null;
126        FileReader fr = null;
127        try
128        {
129            fr = new FileReader(f);
130        }
131        catch(IOException e)
132        {
133            throw new ConnectException
134                ("Unable to read OpenModelica Compiler CORBA object from "
135                        + f.toString());
136        }
137
138        br = new BufferedReader(fr);
139           
140        try
141        {
142            stringifiedObjectReference = br.readLine();
143        }
144        catch(IOException e)
145        {
146            throw new ConnectException("Unable to read OpenModelica Compiler"
147                    + " CORBA object from " + getPathToObject());
148        }
149        return stringifiedObjectReference;
150    }
151   
152    /**
153     * @return Returns the path to the OMC CORBA object that is stored on disk.
154     */
155    private static String getPathToObject()
156    {
157        String fileName = null;
158        if(os.equals("Unix"))
159        {
160            /* This mirrors the way OMC creates the object file. */
161            String username = System.getenv("USER");
162            if(username == null)
163            {
164                username = "nobody";
165            }
166            fileName = "/tmp/openmodelica." + username + ".objid";
167        }
168        else if(os.equals("Windows"))
169        {
170            String temp = System.getenv("TMP");         
171            fileName = temp + "\\openmodelica.objid";
172        }
173       
174        logOMCStatus("will look for OMC object reference in '" 
175                + fileName + "'");
176       
177        return fileName;
178    }
179   
180    /**
181     * Start a new OMC server.
182     */
183    private static void startServer() throws ConnectException
184    {
185        String pathToOmc = null;
186
187        /*
188         * Path to omc (or omc.exe) can be found in the OPENMODELICAHOME
189         * variable.
190         */
191        String omHome = System.getenv("OPENMODELICAHOME");
192        if(omHome == null)
193        {
194            final String m = "Environment variable OPENMODELICAPATH not set";
195            logOMCStatus("Environment variable OPENMODELICAPATH not set,"+
196                    " don't know how to start OMC");
197            throw new ConnectException(m);
198        }
199       
200        if(os.equals("Unix"))
201        {
202            pathToOmc = omHome + "/omc";
203        }
204        else if(os.equals("Windows"))
205        {
206            pathToOmc = omHome + "\\omc.exe";
207        }
208
209        /*
210         * Delete old object reference file. We need to do this because we're
211         * checking if the file exists to determine if the server has started
212         * or not (further down).
213         */
214        File f = new File(getPathToObject());
215        if(f.exists())
216        {
217            logOMCStatus("removing old OMC object reference file");
218            f.delete();
219        }
220       
221        String command[] = { pathToOmc, "+d=interactiveCorba" };
222        try
223        {
224            logOMCStatus("running command " + command[0] + " " + command[1]);
225            Runtime.getRuntime().exec(command);
226            logOMCStatus("command run successfully");
227        }
228        catch(IOException e)
229        {
230            /*
231             * If we fail to start the compiler, maybe the executable is in
232             * the Compiler directory (if we've compiled the compiler from
233             * source). Try starting OMC from this secondary location.
234             */
235            logOMCStatus("error running command " + e.getMessage()
236                    + "\ntrying allternative path to the binary");
237            String secondaryPathToOmc = null;
238            try
239            {
240                if(os.equals("Unix"))
241                {
242                    secondaryPathToOmc = omHome + "/Compiler/omc";
243                }
244                else if(os.equals("Windows"))
245                {
246                    secondaryPathToOmc = omHome + "\\Compiler\\omc.exe";
247                }
248
249                command = 
250                    new String[]{secondaryPathToOmc, "+d=interactiveCorba"};
251                logOMCStatus("running command " 
252                        + command[0] + " " + command[1]);
253                Runtime.getRuntime().exec(command);
254                logOMCStatus("command run successfully");
255            }
256            catch(IOException ex)
257            {
258                logOMCStatus("error running command, giving up"); 
259                throw new ConnectException
260                    ("Unable to start Open Modelica Compiler. "
261                     + "Tried starting " + pathToOmc
262                     + " and " + secondaryPathToOmc);
263            }
264        }
265
266        /*
267         * Wait until the object exists on disk, but if it takes longer than
268         * 5 seconds, abort. (Very arbitrary 5 seconds..)
269         */
270        int ticks = 0;
271        while(!f.exists())
272        {
273            try
274            {
275                Thread.sleep(100);
276            }
277            catch(InterruptedException e)
278            {
279                // Ignore
280            }
281            ticks++;
282           
283            /* If we've waited for 5 seconds, abort the wait for OMC */
284            if(ticks > 50)
285            {
286                logOMCStatus("no OMC object reference file created after " + 
287                        "approximatly 5 seconds\n" +
288                        "it seems OMC does not want to come up, giving up");
289                throw new ConnectException
290                    ("Unable to start the Open Modelica Compiler. Waited for 5"
291                            +" seconds, but it didn't respond.");
292            }
293        }
294    }
295   
296    /**
297     * Initializes an ORB, converts the stringified OMC object to a real
298     * CORBA object, and then narrows that object to an OmcCommunication
299     * object.
300     */
301    private static void setupOmcc(String stringifiedObjectReference)
302    {
303        /* Can't remember why this is needed. But it is. */
304        String args[] = {null};
305       
306        ORB orb;
307        orb = ORB.init(args, null);
308       
309        /* Convert string to object. */
310        org.omg.CORBA.Object obj
311            = orb.string_to_object(stringifiedObjectReference);
312       
313        /* Convert object to OmcCommunication object. */
314        omcc = OmcCommunicationHelper.narrow(obj);
315    }
316   
317    /**
318     * @return the name of the operating system. If an unknown os is found,
319     * the default is Unix.
320     */
321    private static String getOs()
322    {
323        String osName = System.getProperty("os.name");
324        if(osName.contains("Linux"))
325        {
326            return "Unix";
327        }
328        else if(osName.contains("Windows"))
329        {
330            return "Windows";
331        }
332        else
333        {
334            ErrorManager.logWarning("'" + osName + "' is unsupported OS");
335            /* If the OS is not GNU/Linux or Windows, default to Unix */
336            return "Unix";
337        }
338    }
339
340    /**
341     * Initialize the communication with OMC
342     * @throws ConnectException if we're unable to start communicating with
343     * the server
344     */
345    private void init() throws ConnectException
346    {
347        /*
348         * Get type of operating system, used for finding object
349         * reference and starting OMC if the reference is faulty
350         */
351        os = getOs();
352       
353        /* See if an OMC server is already running */
354        File f = new File(getPathToObject());
355        String stringifiedObjectReference = null;
356        if(!f.exists())
357        {
358            /* If a server isn't running, start it */
359            logOMCStatus("no OMC object reference found");
360            startServer();
361        }
362        else
363        {
364            logOMCStatus("OMC object reference present," +
365                    " assuming OMC is running");
366        }
367       
368        /* Read in the CORBA OMC object from a file on disk */
369        stringifiedObjectReference = readObjectFromFile();
370
371        /*
372         * Setup up OMC object reference by initializing ORB and then
373         * converting the string object to a real CORBA object.
374         */
375        setupOmcc(stringifiedObjectReference);
376
377        try
378        {
379            /*
380             * Test the server by trying to send an expression to it.
381             * This might fail if the object reference found on disk didn't
382             * have a corresponding server running. If a server is missing,
383             * catch an exception and try starting a server.
384             */
385            logOMCStatus("trying to send expression to OMC");
386            omcc.sendExpression("1+1");
387            logOMCStatus("expression send successfully");
388        }
389        catch(org.omg.CORBA.COMM_FAILURE e)
390        {
391            /* Start server and set up omcc */
392            logOMCStatus("failed sending expression");
393            startServer();
394            stringifiedObjectReference = readObjectFromFile();
395            setupOmcc(stringifiedObjectReference);
396
397            try
398            {
399                /* Once again try to send an expression to OMC. If it fails this
400                 * time it's time to send back an exception to the caller of
401                 * this function. */
402                logOMCStatus("trying to send expression to OMC");
403                omcc.sendExpression("1+1");
404                logOMCStatus("expression send successfully");
405            }
406            catch(org.omg.CORBA.COMM_FAILURE x)
407            {
408                logOMCStatus("failed sending expression, giving up");
409                throw new ConnectException("Unable to start the OpenModelica"
410                        +" Compiler.");
411            }
412        }
413
414        hasInitialized = true;
415    }
416   
417    /**
418     * Send expression to OMC. If communication is not initialized, it
419     * is initialized here.
420     * @param exp the expression to send to OMC
421     * @throws ConnectException if we're unable to start communicating with
422     * the server
423     */
424    // TODO add synchronization so that two threads don't fudge up each others
425    // communication with OMC
426    // old synchronization aka 'private synchronized String sendExpression(String exp)'
427    // doesnt work when there is possibility of multiple instances of OMCProxy objects
428    private String sendExpression(String exp)
429        throws ConnectException
430    {
431        String retval = null;
432       
433        if(hasInitialized == false)
434        {
435            init();
436        }
437       
438        try
439        {
440            logOMCCall(exp);
441            retval = omcc.sendExpression(exp);
442            logOMCReply(retval);
443        }
444        catch(org.omg.CORBA.COMM_FAILURE x)
445        {
446            /* lost connection to OMC or something */
447            throw new ConnectException("Couldn't send expression to the "+
448                    "OpenModelica Compiler. Tried sending: " + exp);
449        }
450       
451        return retval;
452    }
453   
454    /**
455     * Logs the expression sent to OMC if the
456     * tracing flag (traceOMCCalls) is set
457     *
458     * @param expression the expression that is about to be sent to OMC
459     */
460    private static void logOMCCall(String expression)
461    {
462        if (!traceOMCCalls)
463        {
464            return;
465        }
466        System.out.println(">> " + expression);
467    }
468   
469    /**
470     * loggs the message conserning OMC status if the
471     * tracing flag traceOMCStatus is set
472     * @param message the message to log
473     */
474    private static void logOMCStatus(String message)
475    {
476        if (!traceOMCStatus)
477        {
478            return;
479        }
480        System.out.println(message);
481    }
482
483    /**
484     * Logs the reply received from OMC if
485     * the tracing flag (traceOMCCalls) is set
486     *
487     * @param reply the reply recieved from the OMC
488     */
489    private static void logOMCReply(String reply)
490    {
491        if (!traceOMCCalls)
492        {
493            return;
494        }
495
496        StringTokenizer tokenizer = new StringTokenizer(reply, "\n");
497       
498        while (tokenizer.hasMoreTokens())
499        {
500            System.out.println("<< " + tokenizer.nextToken());
501        }
502    }
503
504    /**
505     * Loads in the Modelica System Library.
506     * @throws ConnectException if we're unable to start communicating with
507     * the server
508     */
509    public void loadSystemLibrary()
510        throws ConnectException
511    {
512        if (!systemLibraryLoaded)
513        {
514            sendExpression("loadModel(Modelica)");
515            systemLibraryLoaded = true;
516        }
517    }
518   
519    /**
520     * Get the classes contained in a class (a package is a class..)
521     *
522     *
523     * @param className full class name where to look for packages
524     * @return an array of subclasses defined (and loaded into OMC)
525     *  inside the class named className, but don't return packages in this
526     *  class. The results is returned as Vector of objects but objects
527     *  are actually String's.
528     * 
529     * @throws ConnectException
530     * @throws UnexpectedReplyException
531     * @throws InitializationException
532     */ 
533    public Vector<Object> getClassNames(String className)
534        throws ConnectException, UnexpectedReplyException
535    {
536        String retval = sendExpression("getClassNames("+className+")");
537       
538        return ModelicaParser.parseList(retval);
539    }
540
541    /**
542     * Gets the restriction type of a class.
543     *
544     * @param className fully qualified class name
545     * @return the restriction type of the class or Type.CLASS if
546     *         type can't be determined
547     * @throws ConnectException
548     */
549    public IModelicaClass.Type getRestrictionType(String className)
550        throws ConnectException
551    {
552        String reply = 
553            sendExpression("getClassRestriction(" + className + ")");
554       
555        /* remove " around the reply */
556        reply = reply.trim();
557        reply = reply.substring(1, reply.length()-1);
558       
559        return IModelicaClass.Type.parse(reply);
560    }
561   
562    /**
563     * Fetches the error string from OMC. This should be called after an "Error"
564     * is received.
565     * @return
566     * @throws ConnectException
567     */
568    private String getErrorString()
569        throws ConnectException
570    {
571        String res = sendExpression("getErrorString()");
572        if(res != null)
573        {
574            res = res.trim();
575            return res.substring(1, res.length() - 1);
576        }
577        else
578            return "";
579    }
580   
581
582    /**
583     * Tries to load file into OMC which causes it to be parsed and the syntax
584     * checked.
585     * @param file the file we want to load
586     * @return either returns the classes (and packages) found in the file or
587     * the error messages from OMC
588     * @throws ConnectException
589     * @throws UnexpectedReplyException
590     * @throws InitializationException
591     */
592    public ParseResults loadFileInteractive(IFile file)
593        throws ConnectException, UnexpectedReplyException
594    {
595        ParseResults res = new ParseResults();
596       
597        String fullName = file.getLocation().toString();
598        String retval = 
599            sendExpression("loadFileInteractive(\"" + fullName + "\")");
600       
601        /*
602         * At this point OMC (ver 1.3.1) does not support returning partial
603         * parsing results if there was parsing errors in the file.
604         * Nor does OMC provide an interface to both query for file contents and
605         * parsing errors (loadFileInteractive() either returns a class name
606         * list or "error").
607         *
608         * So for now we either return parsing errors or class names defined
609         * in the file. Go PELAB!
610         */
611       
612        /*
613         * See if there were parse errors
614         */
615        if(retval.toLowerCase().contains("error"))
616        {
617            res.setCompileErrors
618                (OMCParser.parseErrorString(getErrorString()));
619        }
620        /*
621         * file loaded and parse successsfully
622         */
623        else
624        {
625            res.setClassNames(ModelicaParser.parseList(retval));
626        }
627       
628        return res;
629    }
630
631    /**
632     * Gets the location (file, line number and column number) of a Modelica
633     * element.
634     * @param className the element we want to get location of
635     * @return an IElementLocation containing the file, line number and column
636     * number of the given class
637     * @throws ConnectException
638     * @throws UnexpectedReplyException
639     * @throws InvocationError
640     */
641    public ElementLocation getElementLocation(String className)
642        throws ConnectException, UnexpectedReplyException, InvocationError
643    {
644        String retval = sendExpression("getCrefInfo(" + className + ")");
645       
646        if(retval.contains("Error") || retval.contains("error"))
647        {
648            throw new 
649                InvocationError("fetching file position of " + className,
650                        "getCrefInfo(" + className + ")");
651        }
652       
653       
654        /*
655         * The getCrefInfo reply have the following format:
656         *
657         * <file path>,<line number>,<column number>
658         *
659         */
660
661        /* For some reason, the list returned doesn't contain curly braces. */
662        retval = "{" + retval + "}"; 
663
664        Vector<Object> tokens = ModelicaParser.parseList(retval);
665        int line;
666
667        try
668        {
669            line = Integer.parseInt((String)tokens.elementAt(1));
670        }
671        catch (NumberFormatException e)
672        {
673            throw new 
674            UnexpectedReplyException("can't parse getCrefInfo() reply, "+
675                    "unexpected format");
676        }
677       
678        return new ElementLocation((String)tokens.elementAt(0), line);
679    }
680   
681    /**
682     * Queries the compiler if a particular modelica class/package is a package.
683     *
684     * @param className fully qualified name of the class/package
685     * @return true if className is a package false otherwise
686     * @throws ConnectException
687     */
688    public boolean isPackage(String className)
689        throws ConnectException
690    {
691        String retval = sendExpression("isPackage(" + className + ")");
692        return retval.contains("true");
693    }
694   
695    /**
696     * @param className
697     * @return
698     * @throws ConnectException
699     * @throws InvocationError
700     * @throws UnexpectedReplyException
701     */
702    public Vector<Object> getElementsInfo(String className)
703        throws ConnectException, InvocationError, UnexpectedReplyException
704    {
705        String retval = sendExpression("getElementsInfo("+ className +")");
706       
707        /*
708         * we need a efficient way to check if the result is
709         * humongosly huge list or 'Error' or maybe 'error'
710         */
711        for (int i = 0; i < retval.length(); i++)
712        {
713            if (retval.charAt(i) == '{')
714            {
715                /* we found the begining of the list, let's hope for the best */
716                return ModelicaParser.parseList(retval);       
717            }
718            else if (retval.charAt(i) == 'E' || retval.charAt(i) == 'e')
719            {
720                /*
721                 * this is the unreadable way to check if the retval
722                 * equals 'Error' or 'error'
723                 */
724                if (retval.substring(i+1,i+5).equals("rror"))
725                {
726                    throw new 
727                        InvocationError("fetching contents of " + className,
728                                "getElementsInfo("+ className +")");
729                }
730                else
731                {
732                    /* OMC returned someting wierd, panic mode ! */
733                    break;
734                }
735            }
736        }
737        /* we have no idea what OMC returned */
738        throw new UnexpectedReplyException("getElementsInfo("+ className +")" + 
739                        "replays:'" + retval + "'");
740    }
741
742   
743    public String getCompileName()
744    {
745        return "OpenModelica Compiler";
746    }
747}
Note: See TracBrowser for help on using the repository browser.