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.portal.aspectj;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.util.StreamUtil;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.io.InputStream;
025    
026    import java.net.URL;
027    import java.net.URLClassLoader;
028    
029    import java.nio.file.Files;
030    
031    import java.security.ProtectionDomain;
032    
033    import java.util.Arrays;
034    
035    import org.aspectj.bridge.AbortException;
036    
037    /**
038     * @author Shuyang Zhou
039     */
040    public class WeavingClassLoader extends URLClassLoader {
041    
042            public WeavingClassLoader(
043                    URL[] urls, Class<?>[] aspectClasses, File dumpDir) {
044    
045                    super(urls, null);
046    
047                    _dumpDir = dumpDir;
048    
049                    _urlWeavingAdapter = new URLWeavingAdapter(urls, aspectClasses);
050            }
051    
052            @Override
053            protected Class<?> findClass(String name) throws ClassNotFoundException {
054                    String resourcePath = name.replace('.', '/') + ".class";
055    
056                    InputStream inputStream = getResourceAsStream(resourcePath);
057    
058                    byte[] data = null;
059    
060                    try {
061                            if (inputStream == null) {
062    
063                                    // It may be a generated inner class
064    
065                                    data = _urlWeavingAdapter.removeGeneratedClassDate(name);
066                            }
067                            else {
068                                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
069                                            new UnsyncByteArrayOutputStream();
070    
071                                    StreamUtil.transfer(inputStream, unsyncByteArrayOutputStream);
072    
073                                    data = unsyncByteArrayOutputStream.toByteArray();
074                            }
075    
076                            if (data == null) {
077                                    throw new ClassNotFoundException(name);
078                            }
079    
080                            byte[] oldData = data;
081    
082                            try {
083                                    data = _urlWeavingAdapter.weaveClass(name, data, false);
084                            }
085                            catch (AbortException ae) {
086                                    if (_log.isWarnEnabled()) {
087                                            _log.warn("Abort weaving class " + name, ae);
088                                    }
089                            }
090    
091                            if (Arrays.equals(oldData, data)) {
092                                    return _generateClass(name, data);
093                            }
094    
095                            if (_dumpDir != null) {
096                                    File dumpFile = new File(_dumpDir, resourcePath);
097    
098                                    File dumpDir = dumpFile.getParentFile();
099    
100                                    dumpDir.mkdirs();
101    
102                                    Files.write(dumpFile.toPath(), data);
103    
104                                    if (_log.isInfoEnabled()) {
105                                            _log.info(
106                                                    "Woven class " + name + " result in " +
107                                                            dumpFile.getCanonicalPath());
108                                    }
109                            }
110                            else {
111                                    if (_log.isInfoEnabled()) {
112                                            _log.info("Woven class " + name);
113                                    }
114                            }
115    
116                            return _generateClass(name, data);
117                    }
118                    catch (IOException ioe) {
119                            throw new ClassNotFoundException(name, ioe);
120                    }
121            }
122    
123            private Class<?> _generateClass(String name, byte[] data) {
124                    Class<?> clazz = defineClass(
125                            name, data, 0, data.length, (ProtectionDomain)null);
126    
127                    String packageName = null;
128    
129                    int index = name.lastIndexOf('.');
130    
131                    if (index != -1) {
132                            packageName = name.substring(0, index);
133                    }
134    
135                    if (packageName != null) {
136                            Package pkg = getPackage(packageName);
137    
138                            if (pkg == null) {
139                                    definePackage(
140                                            packageName, null, null, null, null, null, null, null);
141                            }
142                    }
143    
144                    return clazz;
145            }
146    
147            private static final Log _log = LogFactoryUtil.getLog(
148                    WeavingClassLoader.class);
149    
150            private final File _dumpDir;
151            private final URLWeavingAdapter _urlWeavingAdapter;
152    
153    }