source: trunk/modelicaml/org.openmodelica.modelicaml.gen.modelica/src/org/modelica/OMCProxy.java @ 844

Last change on this file since 844 was 844, checked in by wschamai, 13 years ago
  • Doc. header update
  • Modelica <<Import>> stereotype handling
File size: 22.6 KB
Line 
1/*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-CurrentYear, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF GPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL).
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE
13 * OF THE OSMC PUBLIC LICENSE OR THE GPL VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
14 *
15 * The OpenModelica software and the Open Source Modelica
16 * Consortium (OSMC) Public License (OSMC-PL) are obtained
17 * from OSMC, either from the above address,
18 * from the URLs: http://www.ida.liu.se/projects/OpenModelica or 
19 * http://www.openmodelica.org, and in the OpenModelica distribution.
20 * GNU version 3 is obtained from: http://www.gnu.org/copyleft/gpl.html.
21 *
22 * This program is distributed WITHOUT ANY WARRANTY; without
23 * even the implied warranty of  MERCHANTABILITY or FITNESS
24 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
25 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
26 *
27 * See the full OSMC Public License conditions for more details.
28 *
29 * Main author: Wladimir Schamai, EADS Innovation Works / Linköping University, 2009-now
30 *
31 * Contributors:
32 *   Uwe Pohlmann, University of Paderborn 2009-2010, contribution to the Modelica code generation for state machine behavior, contribution to Papyrus GUI adoptations
33 *   Parham Vasaiely, EADS Innovation Works / Hamburg University of Applied Sciences 2009-2011, implementation of simulation plugins
34 */
35
36package org.modelica;
37
38import java.io.BufferedReader;
39import java.io.File;
40import java.io.FileReader;
41import java.io.IOException;
42import java.util.StringTokenizer;
43
44import org.omg.CORBA.ORB;
45
46// TODO: Auto-generated Javadoc
47/**
48 * The OMCProxy is the glue between the OpenModelica Compiler and MDT.
49 * It uses the interactive API of OMC to get information about classes
50 * and to load classes into OMC.
51 *
52 * @author Andreas Remar
53 * @author Adrian Pop
54 */
55public class OMCProxy 
56{
57   
58    /* the CORBA object */
59    /** The omcc. */
60    private static OmcCommunication omcc;
61   
62    /** The corba session name. */
63    private static String corbaSessionName = "ECLIPSE";
64       
65    /**
66     * The Enum osType.
67     */
68    enum osType { /** The WINDOWS. */
69 WINDOWS, /** The UNIX. */
70 UNIX };
71   
72    /* what Operating System we're running on */
73    /** The os. */
74    private static osType os;
75   
76    /* indicates if we've setup the communication with OMC */
77    /** The has initialized. */
78    private static boolean hasInitialized = false;
79   
80    /* indicates if the Modelica System Library has been loaded */
81    /** The system library loaded. */
82    private boolean systemLibraryLoaded = false;
83
84    /** The standard library packages. */
85    private String[] standardLibraryPackages = { "Modelica" };
86
87    /* debug options  */
88    /* should we trace the calls to sendExpression? */
89    /** The trace omc calls. */
90    private static boolean traceOMCCalls = true;
91   
92    /** The trace omc status. */
93    private static boolean traceOMCStatus = true;
94
95    /** The existing corba file is new. */
96    private static boolean existingCorbaFileIsNew = false;
97   
98    /**
99     * Instantiates a new oMC proxy.
100     */
101    public OMCProxy()
102    {
103       
104    }
105
106    /**
107     * Reads in the OMC CORBA object reference from a file on disk.
108     *
109     * @return the object reference as a <code>String</code>
110     * @throws ConnectException
111     *             the connect exception
112     */
113    private static String readObjectFromFile() throws ConnectException
114    {
115        File f = new File(getPathToObject());
116        String stringifiedObjectReference = null;
117
118        BufferedReader br = null;
119        FileReader fr = null;
120        try
121        {
122            fr = new FileReader(f);
123        }
124        catch(IOException e)
125        {
126            throw new ConnectException("Unable to read OpenModelica Compiler CORBA object from " + f.toString());
127        }
128
129        br = new BufferedReader(fr);
130           
131        try
132        {
133            stringifiedObjectReference = br.readLine();
134        }
135        catch(IOException e)
136        {
137            throw new ConnectException("Unable to read OpenModelica Compiler"
138                    + " CORBA object from " + getPathToObject());
139        }
140        return stringifiedObjectReference;
141    }
142   
143    /**
144     * Checks if is checks for initialized.
145     *
146     * @return true, if is checks for initialized
147     */
148    public static boolean isHasInitialized() {
149        return hasInitialized;
150    }
151
152    /**
153     * Sets the checks for initialized.
154     *
155     * @param has
156     *            the new checks for initialized
157     */
158    public static void setHasInitialized(boolean has) {
159        hasInitialized = has;
160    }
161   
162    /**
163     * Checks if is existing corba file is new.
164     *
165     * @return true, if is existing corba file is new
166     */
167    public static boolean isExistingCorbaFileIsNew() {
168        return existingCorbaFileIsNew;
169    }
170
171    /**
172     * Sets the existing corba file is new.
173     *
174     * @param existingCorbaFileIsNew
175     *            the new existing corba file is new
176     */
177    public static void setExistingCorbaFileIsNew(boolean existingCorbaFileIsNew) {
178        OMCProxy.existingCorbaFileIsNew = existingCorbaFileIsNew;
179    }
180
181    /**
182     * Gets the path to object.
183     *
184     * @return Returns the path to the OMC CORBA object that is stored on disk.
185     */
186    private static String getPathToObject()
187    {
188        String fileName = null;
189
190        /* This mirrors the way OMC creates the object file. */     
191        switch (os)
192        {
193        case UNIX:
194            String username = System.getenv("USER");
195            if(username == null)
196            {
197                username = "nobody";
198            }
199            fileName = "/tmp/openmodelica." + username + ".objid." + corbaSessionName;
200            break;
201        case WINDOWS:
202            String temp = System.getenv("TMP");         
203            fileName = temp + "\\openmodelica.objid." + corbaSessionName;
204            break;
205        default:
206            logBug("org.modelica", "os variable set to unexpected os-type");
207        }
208       
209        logOMCStatus("Will look for OMC object reference in '" 
210                + fileName + "'.");
211       
212        return fileName;
213    }
214   
215    /**
216     * With the help of voodoo magic determines the path to the
217     * omc binary that user (probably) wants to use and the working
218     * direcoty of where that binary (most likely) should be started in
219     *
220     * This will returns for example 'c:\openmodelica132\omc.exe'
221     * or '/usr/local/share/openmodelica/omc' depending on
222     * such factors as: OS type, environment variables settings,
223     * plugin user preferences, where the first matching
224     * binary found and the weather outside.
225     *
226     * @return full path to the omc binary 
227     * @throws ConnectException if the path could not be determined
228     */
229    private static File[] getOmcBinaryPaths() throws ConnectException
230    {
231        String binaryName = "omc";
232       
233        if (os == osType.WINDOWS)
234        {
235            binaryName += ".exe";
236        }
237       
238        File omcBinary = null;
239        File omcWorkingDirectory = null;
240        /*
241         * user specified that standard path to omc should be used,
242         * try to determine the omc path via the OPENMODELICAHOME and
243         * by checking in it's varius subdirectory for the omc binary file
244         */
245        logOMCStatus("Using OPENMODELICAHOME environment variable to find omc-binary");
246       
247        /*
248         * Standard path to omc (or omc.exe) binary is encoded in OPENMODELICAHOME
249         * variable.
250         */
251        String openModelicaHome = System.getenv("OPENMODELICAHOME");
252        if(openModelicaHome == null)
253        {
254            final String m = "Environment variable OPENMODELICAHOME not set";
255            logOMCStatus("Environment variable OPENMODELICAHOME not set,"+
256                    " don't know how to start OMC from standard path.");
257            throw new ConnectException(m);
258        }
259       
260        omcWorkingDirectory = new File(openModelicaHome);
261       
262        /* the subdirectories where omc binary may be located, hurray for standards! */
263        String[] subdirs = { "", "bin", "Compiler" };
264       
265        for (String subdir : subdirs)
266        {
267       
268            String path = omcWorkingDirectory.getAbsolutePath() + File.separator;
269            path += subdir.equals("") ? binaryName :  subdir + File.separator + binaryName;
270
271            File file = new File(path); 
272
273            if (file.exists())
274            {
275                omcBinary = file;
276                logOMCStatus("Using omc-binary at '" + omcBinary.getAbsolutePath() + "'");
277                break;
278            }
279            else
280            {
281                logOMCStatus("No omc binary at: [" + path + "]");
282            }
283        }
284       
285        if (omcBinary == null)
286        {
287            logOMCStatus("Could not fine omc-binary on the OPENMODELICAHOME path");
288            throw new ConnectException("Unable to start the OpenModelica Compiler, binary not found");
289        }
290
291        return new File[] {omcBinary, omcWorkingDirectory};
292    }
293   
294    /**
295     * Start a new OMC server.
296     *
297     * @throws ConnectException
298     *             the connect exception
299     */
300    private static void startServer() throws ConnectException
301    {
302        File tmp[] = getOmcBinaryPaths();
303
304        File omcBinary = tmp[0];
305        File workingDirectory = new File(".");
306
307       
308        /*
309         * Delete old object reference file. We need to do this because we're
310         * checking if the file exists to determine if the server has started
311         * or not (further down).
312         */
313        File f = new File(getPathToObject());
314        long lastModified = 0;     
315        if(f.exists())
316        {
317            lastModified = f.lastModified();
318            logOMCStatus("OMC object reference file is already on disk, we try to use it.");
319            if (existingCorbaFileIsNew) return;
320            logOMCStatus("OMC object reference file is already on disk, but is old, start a new server.");
321        }
322       
323        String command[] = { omcBinary.getAbsolutePath(), "+c=" + corbaSessionName, "+d=interactiveCorba" };
324        try
325        {
326            logOMCStatus("Running command " + command[0] + " " + command[1] + " " + command[2]);
327            logOMCStatus("Setting working directory to " + workingDirectory.getAbsolutePath());
328            ProcessStartThread pt = new ProcessStartThread(command, workingDirectory);
329            pt.start();
330        }
331        catch(Exception e)
332        {
333            e.printStackTrace();
334            logOMCStatus("Error running command " + e.getMessage());
335            logOMCStatus("Unable to start OMC, giving up."); 
336            throw new ConnectException("Unable to start the OpenModelica Compiler. ");
337        }           
338
339        logOMCStatus("Waiting for OMC CORBA object reference to appear on disk.");
340        existingCorbaFileIsNew = true;
341       
342        /*
343         * Wait until the object exists on disk, but if it takes longer than
344         * 5 seconds, abort. (Very arbitrary 5 seconds..)
345         */
346        int ticks = 0;
347        while(!f.exists() || (f.exists() && lastModified == f.lastModified()) )
348        {
349            try
350            {
351                logOMCStatus("Waiting for OMC CORBA object reference to appear on disk ... for "+ (ticks+1) + " seconds");
352                Thread.sleep(100);
353            }
354            catch(InterruptedException e)
355            {
356                /* ignore */
357            }
358            ticks++;
359           
360            /* If we've waited for around 5 seconds, abort the wait for OMC */
361            if(ticks > 50)
362            {
363                logOMCStatus("No OMC object reference file created after approximately 50 seconds.");
364                logOMCStatus("It seems OMC does not want to start, giving up.");
365                throw new ConnectException("Unable to start the Open Modelica Compiler. Waited for 5 seconds, but it didn't respond.");
366            }
367        }
368        logOMCStatus("The new OMC object reference found.");
369    }
370   
371    /**
372     * Initializes an ORB, converts the stringified OMC object to a real CORBA
373     * object, and then narrows that object to an OmcCommunication object.
374     *
375     * @param stringifiedObjectReference
376     *            the new up omcc
377     */
378    private static void setupOmcc(String stringifiedObjectReference)
379    {
380        /* Can't remember why this is needed. But it is. */
381        String args[] = {null};
382       
383        ORB orb;
384        orb = ORB.init(args, null);
385       
386        /* Convert string to object. */
387        org.omg.CORBA.Object obj = orb.string_to_object(stringifiedObjectReference);
388        logOMCStatus("Corba IOR:" + stringifiedObjectReference);
389        /* Convert object to OmcCommunication object. */
390        omcc = OmcCommunicationHelper.narrow(obj);
391    }
392   
393    /**
394     * Gets the os.
395     *
396     * @return the name of the operating system. If an unknown os is found, the
397     *         default is Unix.
398     */
399    private static osType getOs()
400    {
401        String osName = System.getProperty("os.name");
402        if(osName.contains("Linux"))
403        {
404            return osType.UNIX;
405        }
406        else if(osName.contains("Windows"))
407        {
408            return osType.WINDOWS;
409        }
410        else
411        {
412            logWarning("'" + osName + "' not officialy supported OS");
413            /* If the OS is not GNU/Linux or Windows, default to Unix */
414            return osType.UNIX;
415        }
416    }
417
418    /**
419     * Initialize the communication with OMC.
420     *
421     * @throws ConnectException
422     *             if we're unable to start communicating with the server
423     */
424    private void init() throws ConnectException
425    {
426        /*
427         * Get type of operating system, used for finding object
428         * reference and starting OMC if the reference is faulty
429         */
430        os = getOs();
431       
432        /* See if an OMC server is already running */
433        File f = new File(getPathToObject());
434        String stringifiedObjectReference = null;
435        if(!f.exists())
436        {
437            /* If a server isn't running, start it */
438            logOMCStatus("No OMC object reference found, starting server.");
439            startServer();
440        }
441        else
442        {
443            logOMCStatus("Old OMC CORBA object reference present," + " assuming OMC is running.");
444        }
445       
446        /* Read in the CORBA OMC object from a file on disk */
447        stringifiedObjectReference = readObjectFromFile();
448
449        /*
450         * Setup up OMC object reference by initializing ORB and then
451         * converting the string object to a real CORBA object.
452         */
453        setupOmcc(stringifiedObjectReference);
454
455        try
456        {
457            /*
458             * Test the server by trying to send an expression to it.
459             * This might fail if the object reference found on disk didn't
460             * have a corresponding server running. If a server is missing,
461             * catch an exception and try starting a server.
462             */
463            logOMCStatus("Trying to send expression to OMC.");
464            omcc.sendExpression("1+1");
465            logOMCStatus("Expression sent successfully.");
466        }
467        catch(org.omg.CORBA.COMM_FAILURE e)
468        {
469            /* Start server and set up omcc */
470            logOMCStatus("Failed sending expression, will try to start OMC.");
471            existingCorbaFileIsNew = false;
472            startServer();
473            stringifiedObjectReference = readObjectFromFile();
474            setupOmcc(stringifiedObjectReference);
475
476            try
477            {
478                /* Once again try to send an expression to OMC. If it fails this
479                 * time it's time to send back an exception to the caller of
480                 * this function. */
481                logOMCStatus("Trying to send expression to OMC.");
482                omcc.sendExpression("1+1");
483                logOMCStatus("Expression sent successfully.");
484            }
485            catch(org.omg.CORBA.COMM_FAILURE x)
486            {
487                logOMCStatus("Failed sending expression, giving up.");
488                throw new ConnectException("Unable to start the OpenModelica Compiler.");
489            }
490        }
491
492        hasInitialized = true;
493    }
494   
495    /**
496     * Send expression to OMC. If communication is not initialized, it is
497     * initialized here.
498     *
499     * @param exp
500     *            the expression to send to OMC
501     * @return the string
502     * @throws ConnectException
503     *             if we're unable to start communicating with the server
504     */
505    // TODO add synchronization so that two threads don't fudge up each others
506    // communication with OMC
507    // old synchronization aka 'private synchronized String sendExpression(String exp)'
508    // doesnt work when there is possibility of multiple instances of OMCProxy objects
509    public String sendExpression(String exp)
510        throws ConnectException
511    {
512        String retval = null;
513       
514        if(hasInitialized == false)
515        {
516            init();
517        }
518       
519        try
520        {
521            logOMCCall(exp);
522            retval = omcc.sendExpression(exp);
523            logOMCReply(retval);
524        }
525        catch(org.omg.CORBA.COMM_FAILURE x)
526        {
527            logOMCCallError("Error while sending expression " + exp + " ["+x+"]");
528            /* lost connection to OMC or something */
529            throw new ConnectException("Couldn't send expression to the "+
530                    "OpenModelica Compiler. Tried sending: " + exp);
531        }
532       
533        return retval;
534    }
535   
536    /**
537     * Logs the expression sent to OMC if the tracing flag (traceOMCCalls) is
538     * set.
539     *
540     * @param expression
541     *            the expression that is about to be sent to OMC
542     */
543    public static void logOMCCall(String expression)
544    {
545        if (!traceOMCCalls)
546        {
547            return;
548        }
549        System.out.println(">> " + expression);
550    }
551   
552    /**
553     * outputs the message about a call error that occured when communicating
554     * with omc.
555     *
556     * @param message
557     *            the message to log
558     */
559    public static void logOMCCallError(String message)
560    {
561        if(!traceOMCCalls)
562        {
563            return;
564        }
565        System.out.println(message);
566    }
567   
568    /**
569     * loggs the message conserning OMC status if the tracing flag
570     * traceOMCStatus is set.
571     *
572     * @param message
573     *            the message to log
574     */
575    public static void logOMCStatus(String message)
576    {
577        if (!traceOMCStatus)
578        {
579            return;
580        }
581        System.out.println("OMCSTATUS: " + message);
582    }
583
584    /**
585     * Logs the reply received from OMC if the tracing flag (traceOMCCalls) is
586     * set.
587     *
588     * @param reply
589     *            the reply recieved from the OMC
590     */
591    public static void logOMCReply(String reply)
592    {
593        if (!traceOMCCalls)
594        {
595            return;
596        }
597
598        StringTokenizer tokenizer = new StringTokenizer(reply, "\n");
599       
600        while (tokenizer.hasMoreTokens())
601        {
602            System.out.println("<< " + tokenizer.nextToken());
603        }
604    }
605   
606    /**
607     * Get the classes contained in a class (a package is a class..)
608     *
609     * @param className
610     *            full class name where to look for packages
611     * @return a <code>List</code> of subclasses defined (and loaded into OMC)
612     *         inside the class named className.
613     * @throws ConnectException
614     *             the connect exception
615     */ 
616    public String getClassNames(String className) throws ConnectException
617    {
618        String retval = sendExpression("getClassNames("+className+")");
619       
620        /* fetch error string but ignore it */
621        getErrorString();
622               
623        return retval;
624    }
625
626    /**
627     * Gets the type of restriction of a class.
628     *
629     * @param className
630     *            fully qualified class name
631     * @return the type of restriction of the class
632     * @throws ConnectException
633     *             the connect exception
634     */
635    public String getRestriction(String className) throws ConnectException
636    {
637        String reply = sendExpression("getClassRestriction(" + className + ")");
638
639        /* remove " around the reply */
640        reply = reply.trim();
641               
642        return reply;
643    }
644   
645    /**
646     * Fetches the error string from OMC. This should be called after an "Error"
647     * is received. (Or whenever the queue of errors should be emptied.)
648     *
649     * @return the <code>String</code> of errors
650     * @throws ConnectException
651     *             the connect exception
652     */
653    private String getErrorString() throws ConnectException
654    {
655        String res = sendExpression("getErrorString()");
656       
657        /* Make sure the error string isn't empty */
658        if(res != null && res.length() > 2)
659        {
660            res = res.trim();
661            return res.substring(1, res.length() - 1);
662        }
663        else
664            return "";
665    }
666   
667
668    /**
669     * Tries to load file into OMC which causes it to be parsed and the syntax
670     * checked.
671     *
672     * @param file
673     *            the file we want to load
674     * @return a <code>ParseResult</code> containing the classes found in the
675     *         file and the error messages from OMC
676     * @throws ConnectException
677     *             the connect exception
678     */
679    public String loadSourceFile(String file) throws ConnectException
680    {
681        String retval = sendExpression("loadFileInteractiveQualified(\"" + file + "\")");
682        retval = retval.trim();
683        // String errorString = getErrorString();
684        /*
685         * See if there were parse errors, an empty list {} also denotes error
686         */
687        return retval;
688    }
689
690    /**
691     * Gets the location (file, starting and ending line number and column
692     * number) of a Modelica element.
693     *
694     * @param className
695     *            the element we want to get location of
696     * @return an <code>ElementLocation</code> containing the file, starting and
697     *         ending line number and column number of the given class
698     * @throws ConnectException
699     *             the connect exception
700     * @throws InvocationError
701     *             the invocation error
702     */
703    public String getClassLocation(String className) throws ConnectException, InvocationError
704    {
705        String retval = sendExpression("getCrefInfo(" + className + ")");
706       
707        /* fetch error string but ignore it */
708        getErrorString();
709       
710        if(retval.contains("Error") || retval.contains("error"))
711        {
712            throw new 
713                InvocationError("Fetching file position of " + className, "getCrefInfo(" + className + ")");
714        }
715       
716       
717        /*
718         * The getCrefInfo reply has the following format:
719         *
720         * <file path>,<something>,<start line>,<start column>,<end line>,<end column>
721         *
722         * for example:
723         * /foo/Modelica/package.mo,writable,1,1,1029,13
724         */
725
726        /* For some reason, the list returned doesn't contain curly braces. */
727        retval = retval.trim();
728        retval = "{" + retval + "}"; 
729               
730        return retval;
731    }
732   
733    /**
734     * Queries the compiler if a particular modelica class/package is a package.
735     *
736     * @param className
737     *            fully qualified name of the class/package
738     * @return true if className is a package, false otherwise
739     * @throws ConnectException
740     *             the connect exception
741     */
742    public boolean isPackage(String className) throws ConnectException
743    {
744        String retval = sendExpression("isPackage(" + className + ")");
745
746        /* fetch error string but ignore it */
747        getErrorString();
748       
749        return retval.contains("true");
750    }
751   
752    /**
753     * Uses the OMC API call getElementsInfo to fetch lots of information about
754     * a class definition. See interactive_api.txt in the OMC source tree.
755     *
756     * @param className
757     *            the fully qualified name of a class
758     * @return a <code>Collection</code> (of <code>ElementsInfo</code>)
759     *         containing the information about className
760     * @throws ConnectException
761     *             the connect exception
762     * @throws InvocationError
763     *             the invocation error
764     */
765    public String getElements(String className) throws ConnectException, InvocationError
766    {
767        String retval = sendExpression("getElementsInfo("+ className +")");
768       
769        /* fetch error string */
770        getErrorString();
771       
772        return retval;
773    }
774
775    /**
776     * Gets the class info.
777     *
778     * @param className
779     *            the class name
780     * @return the class info
781     * @throws ConnectException
782     *             the connect exception
783     */
784    public String getClassInfo(String className) throws ConnectException
785    {
786        String retval = sendExpression("getClassInformation("+ className +")");
787       
788        /* fetch error string but ignore it */
789        getErrorString();
790               
791        return  retval;
792    }
793
794   
795    /**
796     * Gets the compiler name.
797     *
798     * @return the name of the compiler that this plugin tries to communicate
799     *         with (at least it tries...)
800     */
801    public String getCompilerName()
802    {
803        return "OpenModelica Compiler";
804    }
805
806    /**
807     * Loads in the Modelica System Library and returns names of the top-level
808     * packages.
809     *
810     * @return the standard library
811     * @throws ConnectException
812     *             if we're unable to start communicating with the server
813     */ 
814    public String[] getStandardLibrary() throws ConnectException
815    {
816        if (!systemLibraryLoaded)
817        {
818            sendExpression("loadModel(Modelica)");
819           
820            /* fetch error string but ignore it */
821            getErrorString();
822           
823            systemLibraryLoaded = true;
824        }
825
826        return standardLibraryPackages;
827    }
828   
829    /**
830     * Log bug.
831     *
832     * @param where
833     *            the where
834     * @param message
835     *            the message
836     */
837    public static void logBug(String where, String message)
838    {
839        System.out.println("Error: " + where + " Message: "+ message);
840    }
841
842    /**
843     * Log warning.
844     *
845     * @param message
846     *            the message
847     */
848    public static void logWarning(String message)
849    {
850        System.out.println("Warning: " + message);
851    }
852
853}
Note: See TracBrowser for help on using the repository browser.