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.kernel.xuggler.XugglerInstallException;
031    import com.liferay.portal.util.PropsUtil;
032    import com.liferay.portal.xuggler.XugglerImpl;
033    
034    import com.xuggle.ferry.JNILibrary;
035    
036    import java.io.File;
037    import java.io.PrintStream;
038    
039    import java.lang.reflect.Field;
040    
041    import java.net.URL;
042    import java.net.URLClassLoader;
043    
044    import java.util.Iterator;
045    import java.util.Properties;
046    import java.util.Set;
047    import java.util.concurrent.Callable;
048    import java.util.logging.Level;
049    import java.util.logging.Logger;
050    import java.util.regex.Matcher;
051    import java.util.regex.Pattern;
052    
053    /**
054     * @author Shuyang Zhou
055     */
056    public class XugglerAutoInstallHelper {
057    
058            public static void installNativeLibraries() throws ProcessException {
059                    if (isNativeLibraryInstalled()) {
060                            if (_log.isDebugEnabled()) {
061                                    _log.debug("Xuggler is already installed");
062                            }
063    
064                            return;
065                    }
066    
067                    String xugglerJarFile = getXugglerJarFileName();
068    
069                    if (xugglerJarFile == null) {
070                            _log.error(
071                                    "Xuggler auto install is not supported on system: " +
072                                            System.getProperty("os.name") + "/" +
073                                                    System.getProperty("os.arch"));
074    
075                            return;
076                    }
077    
078                    ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
079    
080                    PortalClassLoaderUtil.setClassLoader(
081                            ClassLoader.getSystemClassLoader());
082    
083                    try {
084                            Xuggler xuggler = new XugglerImpl();
085    
086                            try {
087                                    xuggler.installNativeLibraries(xugglerJarFile);
088                            }
089                            catch (XugglerInstallException.MustBeURLClassLoader xie) {
090                                    if (_log.isDebugEnabled()) {
091                                            _log.debug(xie, xie);
092                                    }
093                            }
094                            catch (Exception e) {
095                                    throw new ProcessException(e);
096                            }
097    
098                            if (xuggler.isNativeLibraryInstalled()) {
099                                    if (_log.isInfoEnabled()) {
100                                            _log.info("Xuggler installed successfully");
101                                    }
102                            }
103                            else {
104                                    _log.error("Xuggler auto install failed");
105                            }
106                    }
107                    finally {
108                            PortalClassLoaderUtil.setClassLoader(classLoader);
109                    }
110            }
111    
112            public static class IsNativeLibraryInstalledCallable
113                    implements Callable<Boolean> {
114    
115                    @Override
116                    public Boolean call() throws Exception {
117                            PrintStream printStream = System.out;
118    
119                            System.setOut(new PrintStream(new DummyOutputStream()));
120    
121                            Package pkg = JNILibrary.class.getPackage();
122    
123                            Logger logger = Logger.getLogger(pkg.getName());
124    
125                            Level level = logger.getLevel();
126    
127                            logger.setLevel(Level.OFF);
128    
129                            String property = System.getProperty(
130                                    "java.util.logging.config.file");
131    
132                            System.setProperty("java.util.logging.config.file", "configFile");
133    
134                            Xuggler xuggler = new XugglerImpl();
135    
136                            Field informAdministratorField = ReflectionUtil.getDeclaredField(
137                                    XugglerImpl.class, "_informAdministrator");
138    
139                            informAdministratorField.setBoolean(xuggler, false);
140    
141                            try {
142                                    return xuggler.isNativeLibraryInstalled();
143                            }
144                            finally {
145                                    if (property == null) {
146                                            System.clearProperty("java.util.logging.config.file");
147                                    }
148                                    else {
149                                            System.setProperty(
150                                                    "java.util.logging.config.file", property);
151                                    }
152    
153                                    logger.setLevel(level);
154    
155                                    System.setOut(printStream);
156                            }
157                    }
158    
159            }
160    
161            private static String getXugglerJarFileName() {
162                    String bitmode = OSDetector.getBitmode();
163    
164                    if (Validator.isNull(bitmode) ||
165                            (!bitmode.equals("32") && !bitmode.equals("64"))) {
166    
167                            return null;
168                    }
169    
170                    if (OSDetector.isApple()) {
171                            return PropsUtil.get(
172                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-mac"));
173                    }
174    
175                    if (OSDetector.isLinux()) {
176                            return PropsUtil.get(
177                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-linux"));
178                    }
179    
180                    if (OSDetector.isWindows()) {
181                            return PropsUtil.get(
182                                    PropsKeys.XUGGLER_JAR_FILE, new Filter(bitmode + "-win"));
183                    }
184    
185                    return null;
186            }
187    
188            private static boolean isNativeLibraryInstalled() {
189                    Properties properties = PropsUtil.getProperties(
190                            PropsKeys.XUGGLER_JAR_FILE, false);
191    
192                    Set<Object> jarFiles = SetUtil.fromCollection(properties.values());
193    
194                    jarFiles.remove(getXugglerJarFileName());
195    
196                    Thread currentThread = Thread.currentThread();
197    
198                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
199    
200                    Set<URL> urls = ClassPathUtil.getClassPathURLs(contextClassLoader);
201    
202                    Iterator<URL> iterator = urls.iterator();
203    
204                    while (iterator.hasNext()) {
205                            URL url = iterator.next();
206    
207                            String protocol = url.getProtocol();
208    
209                            if (protocol.equals("file")) {
210                                    File file = new File(url.getPath());
211    
212                                    Matcher matcher = _pattern.matcher(file.getName());
213    
214                                    if (matcher.matches()) {
215                                            if (jarFiles.contains(matcher.replaceAll("$1$2"))) {
216                                                    file.delete();
217    
218                                                    iterator.remove();
219                                            }
220                                    }
221                            }
222                    }
223    
224                    URLClassLoader urlClassLoader = new URLClassLoader(
225                            urls.toArray(new URL[urls.size()]), null);
226    
227                    currentThread.setContextClassLoader(urlClassLoader);
228    
229                    try {
230                            Class<Callable<Boolean>> clazz =
231                                    (Class<Callable<Boolean>>)urlClassLoader.loadClass(
232                                            IsNativeLibraryInstalledCallable.class.getName());
233    
234                            Callable<Boolean> callable = clazz.newInstance();
235    
236                            return callable.call();
237                    }
238                    catch (Exception e) {
239                            return false;
240                    }
241                    finally {
242                            currentThread.setContextClassLoader(contextClassLoader);
243                    }
244            }
245    
246            private static final Log _log = LogFactoryUtil.getLog(
247                    XugglerAutoInstallHelper.class);
248    
249            private static final Pattern _pattern = Pattern.compile(
250                    "(.*)-\\d+-\\d+(\\.jar)");
251    
252    }