com.ejisto.modules.controller.wizard.installer.workers.LoadClassAction.java Source code

Java tutorial

Introduction

Here is the source code for com.ejisto.modules.controller.wizard.installer.workers.LoadClassAction.java

Source

/*
 * Ejisto, a powerful developer assistant
 *
 * Copyright (C) 2010-2013 Celestino Bellone
 *
 * Ejisto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ejisto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.ejisto.modules.controller.wizard.installer.workers;

import com.ejisto.core.classloading.decorator.MockedFieldDecorator;
import com.ejisto.core.classloading.util.ReflectionUtils;
import com.ejisto.modules.dao.entities.MockedField;
import com.ejisto.modules.dao.entities.WebApplicationDescriptor;
import com.ejisto.modules.executor.ErrorDescriptor;
import com.ejisto.modules.repository.MockedFieldsRepository;
import javassist.*;
import javassist.bytecode.SignatureAttribute;
import lombok.extern.log4j.Log4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.RecursiveTask;
import java.util.stream.Collectors;

import static com.ejisto.modules.executor.ErrorDescriptor.Category.ERROR;
import static com.ejisto.modules.executor.ErrorDescriptor.Category.WARN;
import static java.util.Collections.emptyList;

/**
 * Created by IntelliJ IDEA.
 * User: celestino
 * Date: 8/8/12
 * Time: 6:21 PM
 */
@Log4j
class LoadClassAction extends RecursiveTask<List<MockedField>> {
    private static final int THRESHOLD = 500;
    private final List<String> classes;
    private final ClassLoader classLoader;
    private final WebApplicationDescriptor webApplicationDescriptor;
    private final ProgressListener listener;
    private final MockedFieldsRepository mockedFieldsRepository;

    private final int from;
    private final int to;

    public LoadClassAction(List<String> classes, ClassLoader classLoader,
            WebApplicationDescriptor webApplicationDescriptor, ProgressListener listener,
            MockedFieldsRepository mockedFieldsRepository) {
        this(classes, classLoader, webApplicationDescriptor, listener, mockedFieldsRepository, 0, classes.size());
    }

    private LoadClassAction(List<String> classes, ClassLoader classLoader,
            WebApplicationDescriptor webApplicationDescriptor, ProgressListener listener,
            MockedFieldsRepository mockedFieldsRepository, int from, int to) {
        this.classes = classes;
        this.classLoader = classLoader;
        this.webApplicationDescriptor = webApplicationDescriptor;
        this.listener = listener;
        this.mockedFieldsRepository = mockedFieldsRepository;
        this.from = from;
        this.to = to;
    }

    @Override
    protected List<MockedField> compute() {
        if (to - from <= 0) {
            return emptyList();
        }
        if (to - from > THRESHOLD) {
            return doParallelWork();
        } else {
            log.debug("attempting to load " + classes.size() + " classes.");
            List<MockedField> results = new ArrayList<>();
            ClassPool classPool = new ClassPool();
            classPool.appendClassPath(new LoaderClassPath(classLoader));
            for (int i = from; i < to; i++) {
                String className = classes.get(i);
                listener.progressChanged(1, className);
                results.addAll(loadClassFields(className, classPool, webApplicationDescriptor));
            }
            log.debug("returning results.");
            return results;
        }
    }

    private List<MockedField> doParallelWork() {
        int collectionSize = to - from;
        int jobs = collectionSize / THRESHOLD + (collectionSize % THRESHOLD == 0 ? 0 : 1);
        log.debug("about to fork " + jobs + " tasks");
        List<LoadClassAction> forkedTasks = new ArrayList<>(jobs);
        List<MockedField> results = new ArrayList<>();
        for (int i = from; i < jobs; i++) {
            int start = THRESHOLD * i;
            int end = Math.min(to, start + THRESHOLD);
            LoadClassAction task = new LoadClassAction(classes, start, end, classLoader, webApplicationDescriptor,
                    listener, mockedFieldsRepository);
            task.fork();
            forkedTasks.add(task);
        }
        log.debug("collecting results");
        forkedTasks.forEach(task -> results.addAll(task.join()));
        log.debug("done");
        return results;
    }

    private List<MockedField> loadClassFields(String className, ClassPool cp, WebApplicationDescriptor descriptor) {
        CtClass clazz = null;
        try {
            clazz = cp.get(className);
            return getMockedFields(clazz, descriptor);
        } catch (Exception | InternalError e) {
            listener.errorOccurred(buildErrorDescriptor(e));
        } finally {
            if (clazz != null) {
                clazz.detach();
            }
        }
        return emptyList();
    }

    private List<MockedField> getMockedFields(CtClass clazz, WebApplicationDescriptor descriptor) {
        List<MockedField> results;
        try {
            results = Arrays.stream(clazz.getDeclaredFields()).map(declaredField -> {
                final MockedField existing = mockedFieldsRepository
                        .loadOptional(descriptor.getContextPath(), clazz.getName(), declaredField.getName())
                        .orElse(new MockedFieldDecorator());
                MockedField mockedField = MockedFieldDecorator.copyOf(existing);
                mockedField.setContextPath(descriptor.getKey());
                mockedField.setClassName(clazz.getName());
                mockedField.setFieldName(declaredField.getName());
                mockedField.setFieldType(getFieldTypeAsString(declaredField));
                parseGenerics(declaredField, mockedField);
                return mockedField;
            }).collect(Collectors.toList());

            CtClass zuperclazz = clazz.getSuperclass();
            if (!zuperclazz.getName().startsWith("java")) {
                results.addAll(getMockedFields(zuperclazz, descriptor));
            }
        } catch (Exception e) {
            listener.errorOccurred(buildErrorDescriptor(e));
            results = Collections.emptyList();
        }
        return results;
    }

    private String getFieldTypeAsString(CtField field) {
        try {
            CtClass type = field.getType();
            if (type.isArray()) {
                //using com.custom.Class[] notation instead of [Lcom.custom.Class; in order to improve
                //readability; standard notation is also supported.
                return type.getComponentType().getName() + "[]";
            }
            return type.getName();
        } catch (NotFoundException e) {
            throw new IllegalStateException(e);
        }
    }

    private ErrorDescriptor buildErrorDescriptor(Throwable e) {
        Class<?> c = e.getClass();
        boolean classIssue = NotFoundException.class.isAssignableFrom(c) || LinkageError.class.isAssignableFrom(c);
        return new ErrorDescriptor(e, classIssue ? WARN : ERROR);
    }

    private void parseGenerics(CtField field, MockedField mockedField) {
        try {
            String encodedSignature = field.getGenericSignature();
            if (StringUtils.isNotEmpty(encodedSignature)) {
                SignatureAttribute.ObjectType decoded = SignatureAttribute.toFieldSignature(encodedSignature);
                if (!SignatureAttribute.TypeVariable.class.isInstance(decoded)) {
                    String signature = decoded.toString();
                    mockedField.setFieldElementType(ReflectionUtils.cleanGenericSignature(signature));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}