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