001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.PrintWriter;
024import java.io.Serializable;
025import java.lang.management.ManagementFactory;
026import java.lang.management.MemoryPoolMXBean;
027import java.lang.management.MemoryType;
028import java.lang.management.MemoryUsage;
029import java.lang.reflect.Field;
030import java.lang.reflect.InvocationTargetException;
031import java.lang.reflect.Method;
032import java.net.InetAddress;
033import java.net.MalformedURLException;
034import java.net.NetworkInterface;
035import java.net.URL;
036import java.net.URLClassLoader;
037import java.nio.charset.Charset;
038import java.util.ArrayList;
039import java.util.Iterator;
040import java.util.LinkedList;
041import java.util.Map;
042import java.util.Map.Entry;
043
044import javax.servlet.ServletContext;
045
046import lucee.aprint;
047import lucee.commons.digest.MD5;
048import lucee.commons.io.log.Log;
049import lucee.commons.io.log.LogUtil;
050import lucee.commons.io.res.Resource;
051import lucee.commons.io.res.ResourceProvider;
052import lucee.commons.io.res.ResourcesImpl;
053import lucee.commons.io.res.util.ResourceUtil;
054import lucee.commons.lang.ClassUtil;
055import lucee.commons.lang.ExceptionUtil;
056import lucee.commons.lang.StringUtil;
057import lucee.loader.TP;
058import lucee.loader.engine.CFMLEngineFactory;
059import lucee.runtime.Info;
060import lucee.runtime.PageContext;
061import lucee.runtime.PageContextImpl;
062import lucee.runtime.config.Config;
063import lucee.runtime.exp.ApplicationException;
064import lucee.runtime.exp.DatabaseException;
065import lucee.runtime.exp.StopException;
066import lucee.runtime.functions.other.CreateUniqueId;
067import lucee.runtime.net.http.ReqRspUtil;
068import lucee.runtime.op.Caster;
069import lucee.runtime.type.Array;
070import lucee.runtime.type.Collection;
071import lucee.runtime.type.KeyImpl;
072import lucee.runtime.type.Query;
073import lucee.runtime.type.QueryImpl;
074import lucee.runtime.type.Struct;
075import lucee.runtime.type.StructImpl;
076import lucee.runtime.type.util.KeyConstants;
077import lucee.runtime.type.util.ListUtil;
078
079import com.jezhumble.javasysmon.CpuTimes;
080import com.jezhumble.javasysmon.JavaSysMon;
081import com.jezhumble.javasysmon.MemoryStats;
082
083/**
084 * 
085 */
086public final class SystemUtil {
087
088        public static final int MEMORY_TYPE_ALL=0;
089        public static final int MEMORY_TYPE_HEAP=1;
090        public static final int MEMORY_TYPE_NON_HEAP=2;
091
092        public static final int ARCH_UNKNOW=0;
093        public static final int ARCH_32=32;
094        public static final int ARCH_64=64;
095
096        public static final char CHAR_DOLLAR=(char)36;
097        public static final char CHAR_POUND=(char)163;
098        public static final char CHAR_EURO=(char)8364;
099        
100
101        public static final int JAVA_VERSION_1_0 = 0;
102        public static final int JAVA_VERSION_1_1 = 1;
103        public static final int JAVA_VERSION_1_2 = 2;
104        public static final int JAVA_VERSION_1_3 = 3;
105        public static final int JAVA_VERSION_1_4 = 4;
106        public static final int JAVA_VERSION_1_5 = 5;
107        public static final int JAVA_VERSION_1_6 = 6;
108        public static final int JAVA_VERSION_1_7 = 7;
109        public static final int JAVA_VERSION_1_8 = 8;
110        public static final int JAVA_VERSION_1_9 = 9;
111        
112
113        public static final int OUT = 0;
114        public static final int ERR = 1;
115        
116        private static final PrintWriter PRINTWRITER_OUT = new PrintWriter(System.out);
117        private static final PrintWriter PRINTWRITER_ERR = new PrintWriter(System.err);
118        
119        private static PrintWriter[] printWriter=new PrintWriter[2];
120    
121    
122        private static final boolean isWindows=System.getProperty("os.name").toLowerCase().startsWith("windows");
123    private static final boolean isUnix=!isWindows &&  File.separatorChar == '/';
124    
125    private static final String JAVA_VERSION_STRING = System.getProperty("java.version");
126    public static final int JAVA_VERSION;
127    static {
128        if(JAVA_VERSION_STRING.startsWith("1.9."))              JAVA_VERSION=JAVA_VERSION_1_9;
129        else if(JAVA_VERSION_STRING.startsWith("1.8.")) JAVA_VERSION=JAVA_VERSION_1_8;
130        else if(JAVA_VERSION_STRING.startsWith("1.7.")) JAVA_VERSION=JAVA_VERSION_1_7;
131        else if(JAVA_VERSION_STRING.startsWith("1.6.")) JAVA_VERSION=JAVA_VERSION_1_6;
132        else if(JAVA_VERSION_STRING.startsWith("1.5.")) JAVA_VERSION=JAVA_VERSION_1_5;
133        else if(JAVA_VERSION_STRING.startsWith("1.4.")) JAVA_VERSION=JAVA_VERSION_1_4;
134        else if(JAVA_VERSION_STRING.startsWith("1.3.")) JAVA_VERSION=JAVA_VERSION_1_3;
135        else if(JAVA_VERSION_STRING.startsWith("1.2.")) JAVA_VERSION=JAVA_VERSION_1_2;
136        else if(JAVA_VERSION_STRING.startsWith("1.1.")) JAVA_VERSION=JAVA_VERSION_1_1;
137        else JAVA_VERSION=JAVA_VERSION_1_0;
138    }
139        
140    private static Resource tempFile;
141    private static Resource homeFile;
142    private static Resource[] classPathes;
143    private static Charset charset;
144    private static String lineSeparator=System.getProperty("line.separator","\n");
145    private static MemoryPoolMXBean permGenSpaceBean;
146
147        public static int osArch=-1;
148        public static int jreArch=-1;
149        
150        static {
151                String strCharset=System.getProperty("file.encoding");
152                if(strCharset==null || strCharset.equalsIgnoreCase("MacRoman"))
153                        strCharset="cp1252";
154
155                if(strCharset.equalsIgnoreCase("utf-8")) charset=CharsetUtil.UTF8;
156                else if(strCharset.equalsIgnoreCase("iso-8859-1")) charset=CharsetUtil.ISO88591;
157                else charset=CharsetUtil.toCharset(strCharset,null);
158                
159                // Perm Gen
160                permGenSpaceBean=getPermGenSpaceBean();
161                // make sure the JVM does not always a new bean
162                MemoryPoolMXBean tmp = getPermGenSpaceBean();
163                if(tmp!=permGenSpaceBean)permGenSpaceBean=null;
164        }
165        
166        
167        
168        public static MemoryPoolMXBean getPermGenSpaceBean() {
169                java.util.List<MemoryPoolMXBean> manager = ManagementFactory.getMemoryPoolMXBeans();
170                MemoryPoolMXBean bean;
171                // PERM GEN
172                Iterator<MemoryPoolMXBean> it = manager.iterator();
173                while(it.hasNext()){
174                        bean = it.next();
175                        if("Perm Gen".equalsIgnoreCase(bean.getName()) || "CMS Perm Gen".equalsIgnoreCase(bean.getName())) {
176                                return bean;
177                        }
178                }
179                it = manager.iterator();
180                while(it.hasNext()){
181                        bean = it.next();
182                        if(StringUtil.indexOfIgnoreCase(bean.getName(),"Perm Gen")!=-1 || StringUtil.indexOfIgnoreCase(bean.getName(),"PermGen")!=-1) {
183                                return bean;
184                        }
185                }
186                // take none-heap when only one
187                it = manager.iterator();
188                LinkedList<MemoryPoolMXBean> beans=new LinkedList<MemoryPoolMXBean>();
189                while(it.hasNext()){
190                        bean = it.next();
191                        if(bean.getType().equals(MemoryType.NON_HEAP)) {
192                                beans.add(bean);
193                                return bean;
194                        }
195                }
196                if(beans.size()==1) return beans.getFirst();
197                
198                // Class Memory/ClassBlock Memory?
199                it = manager.iterator();
200                while(it.hasNext()){
201                        bean = it.next();
202                        if(StringUtil.indexOfIgnoreCase(bean.getName(),"Class Memory")!=-1) {
203                                return bean;
204                        }
205                }
206                
207                
208                return null;
209        }
210        
211    private static Boolean isFSCaseSensitive;
212        private static JavaSysMon jsm;
213        private static Boolean isCLI;
214        private static double loaderVersion=0D;
215        private static String macAddress; 
216
217    /**
218     * returns if the file system case sensitive or not
219     * @return is the file system case sensitive or not
220     */
221    public static boolean isFSCaseSensitive() { 
222        if(isFSCaseSensitive==null) { 
223                try { 
224                        _isFSCaseSensitive(File.createTempFile("abcx","txt"));
225                } 
226                catch (IOException e) { 
227                        File f = new File("abcx.txt").getAbsoluteFile();
228                        try {
229                                                f.createNewFile();
230                            _isFSCaseSensitive(f);
231                                                
232                                        } catch (IOException e1) {
233                                                throw new RuntimeException(e1.getMessage());
234                                        }
235                } 
236        } 
237        return isFSCaseSensitive.booleanValue(); 
238    }
239    private static void _isFSCaseSensitive(File f) { 
240        File temp=new File(f.getPath().toUpperCase()); 
241        isFSCaseSensitive=temp.exists()?Boolean.FALSE:Boolean.TRUE; 
242        f.delete(); 
243    }
244
245
246        /**
247         * fixes a java canonical path to a Windows path
248         * e.g. /C:/Windows/System32 will be changed to C:\Windows\System32
249         *
250         * @param path
251         * @return
252         */
253        public static String fixWindowsPath(String path) {
254                if ( isWindows && path.length() > 3 && path.charAt(0) == '/' && path.charAt(2) == ':' ) {
255                        path = path.substring(1).replace( '/', '\\' );
256                }
257                return path;
258        }
259
260
261    /**
262     * @return is local machine a Windows Machine
263     */
264    public static boolean isWindows() {
265        return isWindows;
266    }
267
268    /**
269     * @return is local machine a Unix Machine
270     */
271    public static boolean isUnix() {
272        return isUnix;
273    }
274
275    /**
276     * @return return System directory
277     */
278    public static Resource getSystemDirectory() {
279        String pathes=System.getProperty("java.library.path");
280        ResourceProvider fr = ResourcesImpl.getFileResourceProvider();
281        if(pathes!=null) {
282            String[] arr=ListUtil.toStringArrayEL(ListUtil.listToArray(pathes,File.pathSeparatorChar));
283            for(int i=0;i<arr.length;i++) {    
284                if(arr[i].toLowerCase().indexOf("windows\\system")!=-1) {
285                    Resource file = fr.getResource(arr[i]);
286                    if(file.exists() && file.isDirectory() && file.isWriteable()) return ResourceUtil.getCanonicalResourceEL(file);
287                    
288                }
289            }
290            for(int i=0;i<arr.length;i++) {    
291                if(arr[i].toLowerCase().indexOf("windows")!=-1) {
292                        Resource file = fr.getResource(arr[i]);
293                    if(file.exists() && file.isDirectory() && file.isWriteable()) return ResourceUtil.getCanonicalResourceEL(file);
294                    
295                }
296            }
297            for(int i=0;i<arr.length;i++) {    
298                if(arr[i].toLowerCase().indexOf("winnt")!=-1) {
299                        Resource file = fr.getResource(arr[i]);
300                    if(file.exists() && file.isDirectory() && file.isWriteable()) return ResourceUtil.getCanonicalResourceEL(file);
301                    
302                }
303            }
304            for(int i=0;i<arr.length;i++) {    
305                if(arr[i].toLowerCase().indexOf("win")!=-1) {
306                        Resource file = fr.getResource(arr[i]);
307                    if(file.exists() && file.isDirectory() && file.isWriteable()) return ResourceUtil.getCanonicalResourceEL(file);
308                    
309                }
310            }
311            for(int i=0;i<arr.length;i++) {
312                Resource file = fr.getResource(arr[i]);
313                if(file.exists() && file.isDirectory() && file.isWriteable()) return ResourceUtil.getCanonicalResourceEL(file);
314            }
315        }
316        return null;
317    }
318    
319    /**
320     * @return return running context root
321     */
322    public static Resource getRuningContextRoot() {
323        ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
324        
325        try {
326            return frp.getResource(".").getCanonicalResource();
327        } catch (IOException e) {}
328        URL url=new Info().getClass().getClassLoader().getResource(".");
329        try {
330            return frp.getResource(FileUtil.URLToFile(url).getAbsolutePath());
331        } catch (MalformedURLException e) {
332            return null;
333        }
334    }
335    
336    /**
337     * returns the Temp Directory of the System
338     * @return temp directory
339     */
340    public static Resource getTempDirectory() {
341        if(tempFile!=null) return tempFile;
342        ResourceProvider fr = ResourcesImpl.getFileResourceProvider();
343        String tmpStr = System.getProperty("java.io.tmpdir");
344        if(tmpStr!=null) {
345            tempFile=fr.getResource(tmpStr);
346            if(tempFile.exists()) {
347                tempFile=ResourceUtil.getCanonicalResourceEL(tempFile);
348                return tempFile;
349            }
350        }
351        File tmp =null;
352        try {
353                tmp = File.createTempFile("a","a");
354            tempFile=fr.getResource(tmp.getParent());
355            tempFile=ResourceUtil.getCanonicalResourceEL(tempFile);   
356        }
357        catch(IOException ioe) {}
358        finally {
359                if(tmp!=null)tmp.delete();
360        }
361        return tempFile;
362    }
363    
364
365    /**
366     * returns the a unique temp file (with no auto delete)
367     * @param extension 
368     * @return temp directory
369     * @throws IOException 
370     */
371    public static Resource getTempFile(String extension, boolean touch) throws IOException {
372        String filename=CreateUniqueId.invoke();
373        if(!StringUtil.isEmpty(extension,true)){
374                if(extension.startsWith("."))filename+=extension;
375                else filename+="."+extension;
376        }
377                Resource file = getTempDirectory().getRealResource(filename);
378                if(touch)ResourceUtil.touch(file);
379                return file;
380        }
381    
382    /**
383     * returns the Hoome Directory of the System
384     * @return home directory
385     */
386    public static Resource getHomeDirectory() {
387        if(homeFile!=null) return homeFile;
388        
389        ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
390        
391        String homeStr = System.getProperty("user.home");
392        if(homeStr!=null) {
393            homeFile=frp.getResource(homeStr);
394            homeFile=ResourceUtil.getCanonicalResourceEL(homeFile);
395        }
396        return homeFile;
397    }
398    
399    public static Resource getClassLoadeDirectory(){
400        return ResourceUtil.toResource(CFMLEngineFactory.getClassLoaderRoot(TP.class.getClassLoader()));
401    }
402
403    /**
404     * get class pathes from all url ClassLoaders
405     * @param ucl URL Class Loader
406     * @param pathes Hashmap with allpathes
407     */
408    private static void getClassPathesFromClassLoader(URLClassLoader ucl, ArrayList pathes) {
409        ClassLoader pcl=ucl.getParent();
410        // parent first
411        if(pcl instanceof URLClassLoader)
412            getClassPathesFromClassLoader((URLClassLoader) pcl, pathes);
413
414        ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
415        // get all pathes
416        URL[] urls=ucl.getURLs();
417        for(int i=0;i<urls.length;i++) {
418            Resource file=frp.getResource(urls[i].getPath());
419            if(file.exists())
420                pathes.add(ResourceUtil.getCanonicalResourceEL(file));
421        }
422        
423    }
424    
425    /**
426     * @return returns a string list of all pathes
427     */
428    public static Resource[] getClassPathes() {
429        
430        if(classPathes!=null) 
431            return classPathes;
432        
433        ArrayList pathes=new ArrayList();
434        String pathSeperator=System.getProperty("path.separator");
435        if(pathSeperator==null)pathSeperator=";";
436            
437    // java.ext.dirs
438        ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
439        
440        
441    // pathes from system properties
442        String strPathes=System.getProperty("java.class.path");
443        if(strPathes!=null) {
444            Array arr=ListUtil.listToArrayRemoveEmpty(strPathes,pathSeperator);
445            int len=arr.size();
446            for(int i=1;i<=len;i++) {
447                Resource file=frp.getResource(Caster.toString(arr.get(i,""),"").trim());
448                if(file.exists())
449                    pathes.add(ResourceUtil.getCanonicalResourceEL(file));
450            }
451        }
452        
453        
454    // pathes from url class Loader (dynamic loaded classes)
455        ClassLoader cl = new Info().getClass().getClassLoader();
456        if(cl instanceof URLClassLoader) 
457            getClassPathesFromClassLoader((URLClassLoader) cl, pathes);
458        
459        return classPathes=(Resource[]) pathes.toArray(new Resource[pathes.size()]);
460        
461    }
462
463    public static long getUsedMemory() {
464        Runtime r = Runtime.getRuntime();
465        return r.totalMemory()-r.freeMemory();
466    }
467    public static long getAvailableMemory() {
468        Runtime r = Runtime.getRuntime();
469        return r.freeMemory();
470    }
471
472    /**
473     * replace path placeholder with the real path, placeholders are [{temp-directory},{system-directory},{home-directory}]
474     * @param path
475     * @return updated path
476     */
477    public static String parsePlaceHolder(String path) {
478        if(path==null) return path;
479        // Temp
480        if(path.startsWith("{temp")) {
481            if(path.startsWith("}",5)) path=getTempDirectory().getRealResource(path.substring(6)).toString();
482            else if(path.startsWith("-dir}",5)) path=getTempDirectory().getRealResource(path.substring(10)).toString();
483            else if(path.startsWith("-directory}",5)) path=getTempDirectory().getRealResource(path.substring(16)).toString();
484        }
485        // System
486        else if(path.startsWith("{system")) {
487            if(path.startsWith("}",7)) path=getSystemDirectory().getRealResource(path.substring(8)).toString();
488            else if(path.startsWith("-dir}",7)) path=getSystemDirectory().getRealResource(path.substring(12)).toString();
489            else if(path.startsWith("-directory}",7)) path=getSystemDirectory().getRealResource(path.substring(18)).toString();
490        }
491        // Home
492        else if(path.startsWith("{home")) {
493            if(path.startsWith("}",5)) path=getHomeDirectory().getRealResource(path.substring(6)).toString();
494            else if(path.startsWith("-dir}",5)) path=getHomeDirectory().getRealResource(path.substring(10)).toString();
495            else if(path.startsWith("-directory}",5)) path=getHomeDirectory().getRealResource(path.substring(16)).toString();
496        }
497        // ClassLoaderDir
498        else if(path.startsWith("{classloader")) {
499            if(path.startsWith("}",12)) path=getClassLoadeDirectory().getRealResource(path.substring(13)).toString();
500            else if(path.startsWith("-dir}",12)) path=getClassLoadeDirectory().getRealResource(path.substring(17)).toString();
501            else if(path.startsWith("-directory}",12)) path=getClassLoadeDirectory().getRealResource(path.substring(23)).toString();
502        }
503        return path;
504    }
505    
506    public static String addPlaceHolder(Resource file, String defaultValue) {
507     // Temp
508        String path=addPlaceHolder(getTempDirectory(),file,"{temp-directory}");
509        if(!StringUtil.isEmpty(path)) return path;
510     // System
511        path=addPlaceHolder(getSystemDirectory(),file,"{system-directory}");
512        if(!StringUtil.isEmpty(path)) return path;
513     // Home
514        path=addPlaceHolder(getHomeDirectory(),file,"{home-directory}");
515        if(!StringUtil.isEmpty(path)) return path;
516        
517      
518        return defaultValue;
519    }
520    
521    private static String addPlaceHolder(Resource dir, Resource file,String placeholder) {
522        if(ResourceUtil.isChildOf(file, dir)){
523                try {
524                                return StringUtil.replace(file.getCanonicalPath(), dir.getCanonicalPath(), placeholder, true);
525                        } 
526                catch (IOException e) {}
527        }
528        return null;
529        }
530    
531
532        public static String addPlaceHolder(Resource file,  Config config, String defaultValue) {
533        //ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
534        
535        // temp
536                Resource dir = config.getTempDirectory();
537                String path = addPlaceHolder(dir,file,"{temp-directory}");
538                if(!StringUtil.isEmpty(path)) return path;
539                
540        // Config 
541                dir = config.getConfigDir();
542                path = addPlaceHolder(dir,file,"{lucee-config-directory}");
543                if(!StringUtil.isEmpty(path)) return path;
544
545        /* / Config WEB
546                dir = config.getConfigDir();
547                path = addPlaceHolder(dir,file,"{lucee-server-directory}");
548                if(!StringUtil.isEmpty(path)) return path;
549*/
550        // Web root
551                dir = config.getRootDirectory();
552                path = addPlaceHolder(dir,file,"{web-root-directory}");
553                if(!StringUtil.isEmpty(path)) return path;
554
555        return addPlaceHolder(file, defaultValue);
556    }
557        
558        public static String parsePlaceHolder(String path, ServletContext sc, Map<String,String> labels) {
559                if(path==null) return null;
560        if(path.indexOf('{')!=-1){
561                if((path.indexOf("{web-context-label}"))!=-1){
562                        String id=hash(sc);
563                        
564                        String label=labels.get(id);
565                        if(StringUtil.isEmpty(label)) label=id;
566                        
567                        path=StringUtil.replace(path, "{web-context-label}", label, false);
568                }
569        }
570        return parsePlaceHolder(path, sc);
571    }
572    
573        public static String parsePlaceHolder(String path, ServletContext sc) {
574        ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
575        
576        
577        if(path==null) return null;
578        if(path.indexOf('{')!=-1){
579                if(StringUtil.startsWith(path,'{')){
580                    
581                    // Web Root
582                    if(path.startsWith("{web-root")) {
583                        if(path.startsWith("}",9))                                      path=frp.getResource(ReqRspUtil.getRootPath(sc)).getRealResource(path.substring(10)).toString();
584                        else if(path.startsWith("-dir}",9))             path=frp.getResource(ReqRspUtil.getRootPath(sc)).getRealResource(path.substring(14)).toString();
585                        else if(path.startsWith("-directory}",9))       path=frp.getResource(ReqRspUtil.getRootPath(sc)).getRealResource(path.substring(20)).toString();
586        
587                    }
588                    else path=SystemUtil.parsePlaceHolder(path);
589                }
590                
591                if((path.indexOf("{web-context-hash}"))!=-1){
592                        String id=hash(sc);
593                        path=StringUtil.replace(path, "{web-context-hash}", id, false);
594                }
595        }
596        return path;
597    }
598        
599        public static String hash(ServletContext sc) {
600        String id=null;
601                try {
602                        id=MD5.getDigestAsString(ReqRspUtil.getRootPath(sc));
603                } 
604                catch (IOException e) {}
605                return id;
606    }
607
608    public static Charset getCharset() {
609        return charset;
610    }
611
612        public static void setCharset(String charset) {
613                SystemUtil.charset = CharsetUtil.toCharset(charset);
614        }
615        public static void setCharset(Charset charset) {
616                SystemUtil.charset = charset;
617        }
618
619        public static String getOSSpecificLineSeparator() {
620                return lineSeparator;
621        }
622
623        public static void sleep(int time) {
624                try {
625                        Thread.sleep(time);
626                } catch (InterruptedException e) {}
627        }
628
629        public static void sleep(long time) {
630                try {
631                        Thread.sleep(time);
632                } catch (InterruptedException e) {}
633        }
634        public static void join(Thread t) {
635                try {
636                        t.join();
637                } catch (InterruptedException e) {}
638        }
639        
640        /**
641         * locks the object (synchronized) before calling wait
642         * @param lock
643         * @param timeout
644         * @throws InterruptedException
645         */
646        public static void wait(Object lock, long timeout) {
647                try {
648                        synchronized (lock) {lock.wait(timeout);}
649                } catch (InterruptedException e) {}
650        }
651
652        /**
653         * locks the object (synchronized) before calling wait (no timeout)
654         * @param lock
655         * @throws InterruptedException
656         */
657        public static void wait(Object lock) {
658                try {
659                        synchronized (lock) {lock.wait();}
660                } catch (InterruptedException e) {}
661        }
662        
663        
664        
665        /**
666         * locks the object (synchronized) before calling notify
667         * @param lock
668         * @param timeout
669         * @throws InterruptedException
670         */
671        public static void notify(Object lock) {
672                synchronized (lock) {lock.notify();}
673        }
674        
675        /**
676         * locks the object (synchronized) before calling notifyAll
677         * @param lock
678         * @param timeout
679         * @throws InterruptedException
680         */
681        public static void notifyAll(Object lock) {
682                synchronized (lock) {lock.notifyAll();}
683        }
684
685        /**
686         * return the operating system architecture
687         * @return one of the following SystemUtil.ARCH_UNKNOW, SystemUtil.ARCH_32, SystemUtil.ARCH_64
688         */
689        public static int getOSArch(){
690                if(osArch==-1) {
691                        osArch = toIntArch(System.getProperty("os.arch.data.model"));
692                        if(osArch==ARCH_UNKNOW)osArch = toIntArch(System.getProperty("os.arch"));
693                }
694                return osArch;
695        }
696        
697        /**
698         * return the JRE (Java Runtime Engine) architecture, this can be different from the operating system architecture
699         * @return one of the following SystemUtil.ARCH_UNKNOW, SystemUtil.ARCH_32, SystemUtil.ARCH_64
700         */
701        public static int getJREArch(){
702                if(jreArch==-1) {
703                        jreArch = toIntArch(System.getProperty("sun.arch.data.model"));
704                        if(jreArch==ARCH_UNKNOW)jreArch = toIntArch(System.getProperty("com.ibm.vm.bitmode"));
705                        if(jreArch==ARCH_UNKNOW)jreArch = toIntArch(System.getProperty("java.vm.name"));
706                        if(jreArch==ARCH_UNKNOW) {
707                                int addrSize = getAddressSize();
708                                if(addrSize==4) return ARCH_32;
709                                if(addrSize==8) return ARCH_64;
710                        }
711                        
712                }
713                return jreArch;
714        }
715        
716        private static int toIntArch(String strArch){
717                if(!StringUtil.isEmpty(strArch)) {
718                        if(strArch.indexOf("64")!=-1) return ARCH_64;
719                        if(strArch.indexOf("32")!=-1) return ARCH_32;
720                        if(strArch.indexOf("i386")!=-1) return ARCH_32;
721                        if(strArch.indexOf("x86")!=-1) return ARCH_32;
722                }
723                return ARCH_UNKNOW;
724        }
725        
726
727        
728        public static int getAddressSize() {
729                try {
730                        Class unsafe = ClassUtil.loadClass(null,"sun.misc.Unsafe",null);
731                        if(unsafe==null) return 0;
732                
733                        Field unsafeField = unsafe.getDeclaredField("theUnsafe");
734                    unsafeField.setAccessible(true);
735                    Object obj = unsafeField.get(null);
736                    Method addressSize = unsafe.getMethod("addressSize", new Class[0]);
737                    
738                    Object res = addressSize.invoke(obj, new Object[0]);
739                    return Caster.toIntValue(res,0);
740                }
741                catch(Throwable t){
742                ExceptionUtil.rethrowIfNecessary(t);
743                        return 0;
744                }
745            
746        }
747        /*private static MemoryUsage getPermGenSpaceSize() {
748                MemoryUsage mu = getPermGenSpaceSize(null);
749                if(mu!=null) return mu;
750                
751                // create error message including info about available memory blocks
752                StringBuilder sb=new StringBuilder();
753                java.util.List<MemoryPoolMXBean> manager = ManagementFactory.getMemoryPoolMXBeans();
754                Iterator<MemoryPoolMXBean> it = manager.iterator();
755                MemoryPoolMXBean bean;
756                while(it.hasNext()){
757                        bean = it.next();
758                        if(sb.length()>0)sb.append(", ");
759                        sb.append(bean.getName());
760                }
761                throw new RuntimeException("PermGen Space information not available, available Memory blocks are ["+sb+"]");
762        }*/
763        
764        private static MemoryUsage getPermGenSpaceSize(MemoryUsage defaultValue) {
765                if(permGenSpaceBean!=null) return permGenSpaceBean.getUsage();
766                // create on the fly when the bean is not permanent
767                MemoryPoolMXBean tmp = getPermGenSpaceBean();
768                if(tmp!=null) return tmp.getUsage();
769                
770                return defaultValue;
771        }
772        
773
774        public static long getFreePermGenSpaceSize() {
775                MemoryUsage mu = getPermGenSpaceSize(null);
776                if(mu==null) return -1;
777                
778                long max = mu.getMax();
779                long used = mu.getUsed();
780                if(max<0 || used<0) return -1;
781                return max-used;
782        }
783        
784        public static int getPermGenFreeSpaceAsAPercentageOfAvailable() {       
785                MemoryUsage mu = getPermGenSpaceSize(null);
786                if(mu == null) return -1;
787                
788                long max = mu.getMax();
789                long used = mu.getUsed();
790                if( max < 0 || used < 0) return -1;
791                
792                //return a value that equates to a percentage of available free memory
793                return 100 - ((int)(100 * (((double)used) / ((double)max))));
794        }
795        
796        public static int getFreePermGenSpacePromille() {
797                MemoryUsage mu = getPermGenSpaceSize(null);
798                if(mu==null) return -1;
799                
800                long max = mu.getMax();
801                long used = mu.getUsed();
802                if(max<0 || used<0) return -1;
803                return (int)(1000L-(1000L*used/max));
804        }
805        
806        public static Query getMemoryUsageAsQuery(int type) throws DatabaseException {
807                
808                
809                java.util.List<MemoryPoolMXBean> manager = ManagementFactory.getMemoryPoolMXBeans();
810                Iterator<MemoryPoolMXBean> it = manager.iterator();
811                Query qry=new QueryImpl(new Collection.Key[]{
812                                KeyConstants._name,
813                                KeyConstants._type,
814                                KeyConstants._used,
815                                KeyConstants._max,
816                                KeyConstants._init
817                },0,"memory");
818                
819                int row=0;
820                MemoryPoolMXBean bean;
821                MemoryUsage usage;
822                MemoryType _type;
823                while(it.hasNext()){
824                        bean = it.next();
825                        usage = bean.getUsage();
826                        _type = bean.getType();
827                        if(type==MEMORY_TYPE_HEAP && _type!=MemoryType.HEAP)continue;
828                        if(type==MEMORY_TYPE_NON_HEAP && _type!=MemoryType.NON_HEAP)continue;
829                                
830                        row++;
831                        qry.addRow();
832                        qry.setAtEL(KeyConstants._name, row, bean.getName());
833                        qry.setAtEL(KeyConstants._type, row, _type.name());
834                        qry.setAtEL(KeyConstants._max, row, Caster.toDouble(usage.getMax()));
835                        qry.setAtEL(KeyConstants._used, row, Caster.toDouble(usage.getUsed()));
836                        qry.setAtEL(KeyConstants._init, row, Caster.toDouble(usage.getInit()));
837                        
838                }
839                return qry;
840        }
841        
842        public static Struct getMemoryUsageAsStruct(int type) {
843                java.util.List<MemoryPoolMXBean> manager = ManagementFactory.getMemoryPoolMXBeans();
844                Iterator<MemoryPoolMXBean> it = manager.iterator();
845                
846                MemoryPoolMXBean bean;
847                MemoryUsage usage;
848                MemoryType _type;
849                long used=0,max=0,init=0;
850                while(it.hasNext()){
851                        bean = it.next();
852                        usage = bean.getUsage();
853                        _type = bean.getType();
854                        if((type==MEMORY_TYPE_HEAP && _type==MemoryType.HEAP) || (type==MEMORY_TYPE_NON_HEAP && _type==MemoryType.NON_HEAP)){
855                                used+=usage.getUsed();
856                                max+=usage.getMax();
857                                init+=usage.getInit();
858                        }
859                }
860                Struct sct=new StructImpl();
861                sct.setEL(KeyConstants._used, Caster.toDouble(used));
862                sct.setEL(KeyConstants._max, Caster.toDouble(max));
863                sct.setEL(KeyConstants._init, Caster.toDouble(init));
864                sct.setEL(KeyImpl.init("available"), Caster.toDouble(max-used));
865                return sct;
866        }
867        
868
869        public static Struct getMemoryUsageCompact(int type) {
870                java.util.List<MemoryPoolMXBean> manager = ManagementFactory.getMemoryPoolMXBeans();
871                Iterator<MemoryPoolMXBean> it = manager.iterator();
872                
873                MemoryPoolMXBean bean;
874                MemoryUsage usage;
875                MemoryType _type;
876                Struct sct=new StructImpl();
877                while(it.hasNext()){
878                        bean = it.next();
879                        usage = bean.getUsage();
880                        _type = bean.getType();
881                        if(type==MEMORY_TYPE_HEAP && _type!=MemoryType.HEAP)continue;
882                        if(type==MEMORY_TYPE_NON_HEAP && _type!=MemoryType.NON_HEAP)continue;
883                                
884                        double d=((int)(100D/usage.getMax()*usage.getUsed()))/100D;
885                        sct.setEL(KeyImpl.init(bean.getName()), Caster.toDouble(d));
886                }
887                return sct;
888        }
889        
890        public static String getPropertyEL(String key) {
891                try{
892                        String str = System.getProperty(key);
893                        if(!StringUtil.isEmpty(str,true)) return str;
894                        
895                        Iterator<Entry<Object, Object>> it = System.getProperties().entrySet().iterator();
896                        Entry<Object, Object> e;
897                        String n;
898                        while(it.hasNext()){
899                                e = it.next();
900                                n=(String) e.getKey();
901                                if(key.equalsIgnoreCase(n)) return (String) e.getValue();
902                        }
903                        
904                }
905                catch(Throwable t){
906                ExceptionUtil.rethrowIfNecessary(t);
907        }
908                return null;
909        }
910        public static long microTime() {
911                return System.nanoTime()/1000L;
912        }
913        
914        public static TemplateLine getCurrentContext() {
915                StackTraceElement[] traces = Thread.currentThread().getStackTrace();
916                
917        int line=0;
918                String template;
919                
920                StackTraceElement trace=null;
921                for(int i=0;i<traces.length;i++) {
922                        trace=traces[i];
923                        template=trace.getFileName();
924                        if(trace.getLineNumber()<=0 || template==null || ResourceUtil.getExtension(template,"").equals("java")) continue;
925                        line=trace.getLineNumber();
926                        return new TemplateLine(template,line);
927                }
928                return null;
929        }
930        
931        public static class TemplateLine implements Serializable {
932
933                private static final long serialVersionUID = 6610978291828389799L;
934
935                public final String template;
936                public final int line;
937
938                public TemplateLine(String template, int line) {
939                        this.template=template;
940                        this.line=line;
941                }
942                public String toString(){
943                        return template+":"+line;
944                }
945        }
946
947        public static long getFreeBytes() throws ApplicationException {
948                return physical().getFreeBytes();
949        }
950
951        public static long getTotalBytes() throws ApplicationException {
952                return physical().getTotalBytes();
953        }
954        
955        public static double getCpuUsage(long time) throws ApplicationException {
956                if(time<1) throw new ApplicationException("time has to be bigger than 0");
957                if(jsm==null) jsm=new JavaSysMon();
958                CpuTimes cput = jsm.cpuTimes();
959                if(cput==null) throw new ApplicationException("CPU information are not available for this OS");
960                CpuTimes previous = new CpuTimes(cput.getUserMillis(),cput.getSystemMillis(),cput.getIdleMillis());
961        sleep(time);
962        
963        return jsm.cpuTimes().getCpuUsage(previous)*100D;
964    }
965        
966
967        private synchronized static MemoryStats physical() throws ApplicationException {
968                if(jsm==null) jsm=new JavaSysMon();
969                MemoryStats p = jsm.physical();
970                if(p==null) throw new ApplicationException("Memory information are not available for this OS");
971                return p;
972        }
973        public static void setPrintWriter(int type,PrintWriter pw) {
974                printWriter[type]=pw;
975        }
976        public static PrintWriter getPrintWriter(int type) {
977                if(printWriter[type]==null) {
978                        if(type==OUT) printWriter[OUT]=PRINTWRITER_OUT;
979                        else printWriter[ERR]=PRINTWRITER_ERR;
980                }
981                return printWriter[type];
982        }
983        public static boolean isCLICall() {
984        if(isCLI==null){
985                isCLI=Caster.toBoolean(System.getProperty("lucee.cli.call"),Boolean.FALSE);
986        }
987        return isCLI.booleanValue();
988        }
989        
990        public static double getLoaderVersion() {
991                // this is done via reflection to make it work in older version, where the class lucee.loader.Version does not exist
992                if(loaderVersion==0D) {
993                        loaderVersion=4D;
994                        Class cVersion = ClassUtil.loadClass(TP.class.getClassLoader(),"lucee.loader.Version",null);
995                        if(cVersion!=null) {
996                                try {
997                                        Field f = cVersion.getField("VERSION");
998                                        loaderVersion=f.getDouble(null);
999                                } 
1000                                catch (Throwable t) {
1001                        ExceptionUtil.rethrowIfNecessary(t);
1002                        t.printStackTrace();
1003                    }
1004                        }
1005                }
1006                return loaderVersion;
1007        }
1008        public static String getMacAddress() {
1009                if(macAddress==null) {
1010                        try{
1011                                InetAddress ip = InetAddress.getLocalHost();
1012                                NetworkInterface network = NetworkInterface.getByInetAddress(ip);
1013                                byte[] mac = network.getHardwareAddress();
1014                  
1015                                StringBuilder sb = new StringBuilder();
1016                                for (int i = 0; i < mac.length; i++) {
1017                                        sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));         
1018                                }
1019                                macAddress= sb.toString();
1020                        }
1021                        catch(Throwable t){
1022                ExceptionUtil.rethrowIfNecessary(t);
1023            }
1024                        
1025                }
1026                return macAddress;
1027        }
1028
1029        @Deprecated
1030        public static void stop(Thread thread,Log log) {
1031                new StopThread(thread,new StopException(thread),null).start();
1032        }
1033
1034        public static void stop(PageContext pc,Log log) {
1035                stop(pc,new StopException(pc.getThread()),log);
1036        }
1037        
1038        public static void stop(PageContext pc, Throwable t,Log log) {
1039                new StopThread(pc,t,log).start();
1040        }
1041}
1042
1043class StopThread extends Thread {
1044        
1045        private final PageContext pc;
1046        private final Throwable t;
1047        private final Log log; 
1048        private final Thread thread;
1049
1050        public StopThread(PageContext pc, Throwable t, Log log) {
1051                this.pc=pc;
1052                this.t=t;
1053                this.log=log;
1054                this.thread=pc.getThread();
1055        }
1056        
1057        public StopThread(Thread thread, Throwable t, Log log) {
1058                this.pc=null;
1059                this.t=t;
1060                this.log=log;
1061                this.thread=thread;
1062        }
1063
1064        public void run(){
1065                if(pc!=null){
1066                        PageContextImpl pci=(PageContextImpl) pc;
1067                        pci.stop(t);
1068                }
1069                int count=0;
1070                if(thread!=null && thread.isAlive()) {
1071                        ////do{
1072                                try{
1073                                        if(count>0 && log!=null) {
1074                                                LogUtil.log(log, Log.LEVEL_ERROR, "", "could not stop the thread on the "+count+" approach", thread.getStackTrace());
1075                                        }
1076                                        if(count++>4) {
1077                                                if(log!=null)LogUtil.log(log, Log.LEVEL_ERROR, "", "could not terminate the thread", thread.getStackTrace());
1078                                                aprint.e(thread.getStackTrace());
1079                                                ////break; // should never happen
1080                                        }
1081                                        try{
1082                                                thread.stop(t);
1083                                        }
1084                                        catch(UnsupportedOperationException uoe) {
1085                                                LogUtil.log(log, Log.LEVEL_INFO, "", "Thread.stop(Throwable) is not supported by this JVM and failed with UnsupportedOperationException", thread.getStackTrace());
1086                                                try {
1087                                                        Method m = thread.getClass().getMethod("stop0", new Class[]{Object.class});
1088                                                        m.setAccessible(true); // allow to access private method
1089                                                        m.invoke(thread, new Object[]{t});
1090                                                }
1091                                                catch (Exception e) {
1092                                                        if(
1093                                                                        e instanceof IllegalAccessException || 
1094                                                                        e instanceof IllegalArgumentException || 
1095                                                                        e instanceof InvocationTargetException ||
1096                                                                        e instanceof NoSuchMethodException ||
1097                                                                        e instanceof SecurityException) {
1098                                                                thread.stop();
1099                                                        }
1100                                                        else throw e;
1101                                                }
1102                                        }
1103                                }
1104                                // catch any exception
1105                                catch(Exception e){
1106                                        LogUtil.log(log, Log.LEVEL_ERROR, "", e);
1107                                }
1108                                
1109                                /*if(!(thread.isAlive() && (pc==null || ((PageContextImpl)pc).isInitialized()))) break;
1110                                print.e("sleep-10:"+thread.isAlive());
1111                                SystemUtil.sleep(10);
1112                                
1113                                if(!(thread.isAlive() && (pc==null || ((PageContextImpl)pc).isInitialized()))) break;
1114                                print.e("sleep-100");
1115                                SystemUtil.sleep(100);*/
1116                        ////}while(thread.isAlive() && (pc==null || ((PageContextImpl)pc).isInitialized()));
1117                }
1118
1119        }
1120        
1121}