001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.configuration.Filter;
018    import com.liferay.portal.kernel.io.DummyOutputStream;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.process.ClassPathUtil;
022    import com.liferay.portal.kernel.process.ProcessException;
023    import com.liferay.portal.kernel.util.OSDetector;
024    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
025    import com.liferay.portal.kernel.util.PropsKeys;
026    import com.liferay.portal.kernel.util.ReflectionUtil;
027    import com.liferay.portal.kernel.util.SetUtil;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.xuggler.Xuggler;
030    import com.liferay.portal.util.PropsUtil;
031    import com.liferay.portal.xuggler.XugglerImpl;
032    
033    import com.xuggle.ferry.JNILibrary;
034    
035    import java.io.File;
036    import java.io.PrintStream;
037    
038    import java.lang.reflect.Field;
039    
040    import java.net.URL;
041    import java.net.URLClassLoader;
042    
043    import java.util.Iterator;
044    import java.util.Properties;
045    import java.util.Set;
046    import java.util.concurrent.Callable;
047    import java.util.logging.Level;
048    import java.util.logging.Logger;
049    import java.util.regex.Matcher;
050    import java.util.regex.Pattern;
051    
052    /**
053     * @author Shuyang Zhou
054     */
055    public class XugglerAutoInstallHelper {
056    
057            public static void installNativeLibraries() throws ProcessException {
058                    if (isNativeLibraryInstalled()) {
059                            if (_log.isDebugEnabled()) {
060                                    _log.debug("Xuggler is already installed");
061                            }
062    
063                            return;
064                    }
065    
066                    String xugglerJarFile = getXugglerJarFileName();
067    
068                    if (xugglerJarFile == null) {
069                            _log.error(
070                                    "Xuggler auto install is not supported on system: " +
071                                            System.getProperty("os.name") + "/" +
072                                                    System.getProperty("os.arch"));
073    
074                            return;
075                    }
076    
077                    ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
078    
079                    PortalClassLoaderUtil.setClassLoader(
080                            ClassLoader.getSystemClassLoader());
081    
082                    try {
083                            Xuggler xuggler = new XugglerImpl();
084    
085                            try {
086                                    xuggler.installNativeLibraries(xugglerJarFile, null);
087                            }
088                            catch (Exception e) {
089                                    throw new ProcessException(e);
090                            }
091    
092                            if (xuggler.isNativeLibraryInstalled()) {
093                                    if (_log.isInfoEnabled()) {
094                                            _log.info("Xuggler installed successfully");
095                                    }
096                            }
097                            else {
098                                    _log.error("Xuggler auto install failed");
099                            }
100                    }
101                    finally {
102                            PortalClassLoaderUtil.setClassLoader(classLoader);
103                    }
104            }
105    
106            public static class IsNativeLibraryInstalledCallable
107                    implements Callable<Boolean> {
108    
109                    @Override
110                    public Boolean call() throws Exception {
111                            PrintStream printStream = System.out;
112    
113                            System.setOut(new PrintStream(new DummyOutputStream()));
114    
115                            Package pkg = JNILibrary.class.getPackage();
116    
117                            Logger logger = Logger.getLogger(pkg.getName());
118    
119                            Level level = logger.getLevel();
120    
121                            logger.setLevel(Level.OFF);
122    
123                            String property = System.getProperty(
124                                    "java.util.logging.config.file");
125    
126                            System.setProperty("java.util.logging.config.file", "configFile");
127    
128                            Xuggler xuggler = new XugglerImpl();
129    
130                            Field informAdministratorField = ReflectionUtil.getDeclaredField(
131                                    XugglerImpl.class, "_informAdministrator");
132    
133                            informAdministratorField.setBoolean(xuggler, false);
134    
135                            try {
136                                    return xuggler.isNativeLibraryInstalled();
137                            }
138                            finally {
139                                    if (property == null) {
140                                            System.clearProperty("java.util.logging.config.file");
141                                    }
142                                    else {
143                                            System.setProperty(
144                                                    "java.util.logging.config.file", property);
145                                    }
146    
147                                    logger.setLevel(level);
148    
149                                    System.setOut(printStream);
150                            }
151                    }
152    
153            }
154    
155            private static String getXugglerJarFileName() {
156                    String bitmode = OSDetector.getBitmode();
157    
158                    if (Validator.isNull(bitmode) ||
159                            (!bitmode.equals("32") && !bitmode.equals("64"))) {
160    
161                            return null;
162                    }
163    
164                    if (OSDetector.isApple()) {
165                            return PropsUtil.get(
166                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-mac"));
167                    }
168    
169                    if (OSDetector.isLinux()) {
170                            return PropsUtil.get(
171                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-linux"));
172                    }
173    
174                    if (OSDetector.isWindows()) {
175                            return PropsUtil.get(
176                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-win"));
177                    }
178    
179                    return null;
180            }
181    
182            private static boolean isNativeLibraryInstalled() {
183                    Properties properties = PropsUtil.getProperties(
184                            PropsKeys.XUGGLER_JAR_FILE, false);
185    
186                    Set<Object> jarFiles = SetUtil.fromCollection(properties.values());
187    
188                    jarFiles.remove(getXugglerJarFileName());
189    
190                    Thread currentThread = Thread.currentThread();
191    
192                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
193    
194                    Set<URL> urls = ClassPathUtil.getClassPathURLs(contextClassLoader);
195    
196                    Iterator<URL> iterator = urls.iterator();
197    
198                    while (iterator.hasNext()) {
199                            URL url = iterator.next();
200    
201                            String protocol = url.getProtocol();
202    
203                            if (protocol.equals("file")) {
204                                    File file = new File(url.getPath());
205    
206                                    Matcher matcher = _pattern.matcher(file.getName());
207    
208                                    if (matcher.matches()) {
209                                            if (jarFiles.contains(matcher.replaceAll("$1$2"))) {
210                                                    file.delete();
211    
212                                                    iterator.remove();
213                                            }
214                                    }
215                            }
216                    }
217    
218                    URLClassLoader urlClassLoader = new URLClassLoader(
219                            urls.toArray(new URL[urls.size()]), null);
220    
221                    currentThread.setContextClassLoader(urlClassLoader);
222    
223                    try {
224                            Class<Callable<Boolean>> clazz = (Class<Callable<Boolean>>)
225                                    urlClassLoader.loadClass(
226                                            IsNativeLibraryInstalledCallable.class.getName());
227    
228                            Callable<Boolean> callable = clazz.newInstance();
229    
230                            return callable.call();
231                    }
232                    catch (Exception e) {
233                            return false;
234                    }
235                    finally {
236                            currentThread.setContextClassLoader(contextClassLoader);
237                    }
238            }
239    
240            private static final Log _log = LogFactoryUtil.getLog(
241                    XugglerAutoInstallHelper.class);
242    
243            private static final Pattern _pattern = Pattern.compile(
244                    "(.*)-\\d+-\\d+(\\.jar)");
245    
246    }