package com.intellij.flex.maven;
import org.apache.maven.DefaultMaven;
import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.execution.*;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.properties.internal.EnvironmentUtils;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.rtinfo.RuntimeInformation;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.codehaus.plexus.*;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.impl.LocalRepositoryProvider;
import org.eclipse.aether.internal.impl.DefaultLocalRepositoryProvider;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class GeneratorServer {
private final DefaultPlexusContainer plexusContainer;
private final MavenSession session;
private final DataInputStream in;
private final MavenPluginManager mavenPluginManager;
private final File generatorOutputDirectory;
private final Logger logger;
private final Maven maven;
public static void main(String[] args) throws Exception {
final long start = System.currentTimeMillis();
new GeneratorServer(args);
final long duration = System.currentTimeMillis() - start;
System.out.print("\n[fcg] generating took " + duration + " ms: " + duration / 60000 + " min " + (duration % 60000) / 1000 + "sec");
}
public Logger getLogger() {
return logger;
}
public GeneratorServer(String[] args)
throws ComponentLookupException, IOException, MavenExecutionRequestPopulationException, SettingsBuildingException,
PlexusContainerException, InterruptedException, InvalidRepositoryException {
generatorOutputDirectory = new File(args[4]);
//noinspection ResultOfMethodCallIgnored
generatorOutputDirectory.mkdirs();
in = new DataInputStream(new BufferedInputStream(System.in));
plexusContainer = createPlexusContainer();
logger = plexusContainer.getLoggerManager().getLoggerForComponent(null);
mavenPluginManager = plexusContainer.lookup(MavenPluginManager.class);
session = createSession(createExecutionRequest(args));
maven = new Maven(plexusContainer, session);
final List<String> generators = new ArrayList<String>(2);
final URL generatorJarPath = new File(in.readUTF()).toURI().toURL();
generators.add(in.readUTF());
final int projectsCount = in.readUnsignedShort();
final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try {
for (int i = 0; i < projectsCount; i++) {
final String pathname = in.readUTF();
final String projectId = Integer.toString(i);
executorService.submit(new Runnable() {
@Override
public void run() {
try {
MavenProject project = maven.readProject(new File(pathname), logger);
if (project == null) {
return;
}
String configFilePath = generate(project, generators, generatorJarPath);
synchronized (System.out) {
System.out.append("\n[fcg] generated: ").append(projectId).append(':').append(configFilePath);
for (String sourceRoot : project.getCompileSourceRoots()) {
System.out.append('|').append(sourceRoot);
}
System.out.append("[/fcg]").flush();
}
}
catch (Throwable e) {
getLogger().error("Cannot generate flex config for " + pathname, e);
}
}
});
}
}
finally {
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.MINUTES);
}
}
private void populateBuildNumberPluginFields(MavenProject project, Plugin plugin) throws Exception {
MojoExecution mojoExecution = maven.createMojoExecution(plugin, "create", project);
plexusContainer.lookup(BuildPluginManager.class).executeMojo(session, mojoExecution);
}
private String generate(final MavenProject project, final List<String> generators, final URL generatorJarPath) throws Exception {
session.setCurrentProject(project);
MojoExecution flexmojosMojoExecution = null;
try {
boolean flexmojosGeneratorFound = false;
boolean buildHelperFound = false;
boolean buildNumberFound = false;
for (Plugin plugin : project.getBuildPlugins()) {
final String pluginGroupId = plugin.getGroupId();
if (pluginGroupId.equals("org.sonatype.flexmojos") || pluginGroupId.equals("net.flexmojos.oss")) {
if (flexmojosMojoExecution == null && plugin.getArtifactId().equals("flexmojos-maven-plugin")) {
flexmojosMojoExecution = maven.createMojoExecution(plugin, getCompileGoalName(project), project);
for (Dependency dependency : plugin.getDependencies()) {
if (dependency.getArtifactId().equals("flexmojos-threadlocaltoolkit-wrapper")) {
AdditionalSourceRootUtil.addResourcesAsCompileSourceRoots(project);
break;
}
}
}
else if (!flexmojosGeneratorFound && plugin.getArtifactId().equals("flexmojos-generator-mojo")) {
AdditionalSourceRootUtil.addByGeneratorMojo(maven.createMojoExecution(plugin, "generate", project), session, project, getLogger());
flexmojosGeneratorFound = true;
}
}
else if (!buildHelperFound && plugin.getArtifactId().equals("build-helper-maven-plugin") && pluginGroupId.equals("org.codehaus.mojo")) {
AdditionalSourceRootUtil.addByBuildHelper(maven.createMojoExecution(plugin, "add-source", project), session, project, getLogger());
buildHelperFound = true;
}
else if (!buildNumberFound && plugin.getArtifactId().equals("buildnumber-maven-plugin") && pluginGroupId.equals("org.codehaus.mojo")) {
populateBuildNumberPluginFields(project, plugin);
buildNumberFound = true;
}
if (flexmojosMojoExecution != null && flexmojosGeneratorFound && buildHelperFound && buildNumberFound) {
break;
}
}
AdditionalSourceRootUtil.addByUnknownGeneratorMojo(project);
assert flexmojosMojoExecution != null;
final ClassRealm flexmojosPluginRealm = maven.getPluginRealm(flexmojosMojoExecution);
flexmojosPluginRealm.addURL(generatorJarPath);
final Mojo mojo = mavenPluginManager.getConfiguredMojo(Mojo.class, session, flexmojosMojoExecution);
try {
Class<?> configuratorClass = flexmojosPluginRealm.loadClass(generators.get(0));
FlexConfigGenerator configurator = (FlexConfigGenerator)configuratorClass.getConstructor(MavenSession.class, File.class).newInstance(session, generatorOutputDirectory);
configurator.preGenerate(project, Flexmojos.getClassifier(mojo));
if ("swc".equals(project.getPackaging())) {
configurator.generate(mojo);
}
else {
configurator.generate(mojo, Flexmojos.getSourceFileForSwf(mojo));
}
return configurator.postGenerate(project);
}
finally {
plexusContainer.release(mojo);
}
}
finally {
session.setCurrentProject(null);
if (flexmojosMojoExecution != null) {
maven.releaseMojoExecution(flexmojosMojoExecution);
}
}
}
private static String getCompileGoalName(final MavenProject project) {
return "swc".equals(project.getPackaging()) ? "compile-swc" : "compile-swf";
}
public void resolveOutputs(WorkspaceReaderImpl.ArtifactData data) throws Exception {
final MavenProject project = maven.readProject(data.file, logger);
if (project == null) {
getLogger().warn("Cannot read project while resolve output file for " + data.toString());
return;
}
final MavenProject oldProject = session.getCurrentProject();
MojoExecution flexmojosMojoExecution = null;
try {
session.setCurrentProject(project);
for (Plugin plugin : project.getBuildPlugins()) {
final String pluginGroupId = plugin.getGroupId();
if ((pluginGroupId.equals("org.sonatype.flexmojos") || pluginGroupId.equals("net.flexmojos.oss"))
&& plugin.getArtifactId().equals("flexmojos-maven-plugin")) {
flexmojosMojoExecution = maven.createMojoExecution(plugin, getCompileGoalName(project), project);
break;
}
}
if (flexmojosMojoExecution == null) {
return;
}
// getPluginRealm creates plugin realm and populates pluginDescriptor.classRealm field
maven.getPluginRealm(flexmojosMojoExecution);
Mojo mojo = mavenPluginManager.getConfiguredMojo(Mojo.class, session, flexmojosMojoExecution);
try {
data.outputFile = new File(Flexmojos.getOutput(mojo));
String[] localesRuntime = (String[])Flexmojos.invokePublicMethod(mojo, "getLocalesRuntime");
data.linkReport = Flexmojos.getLinkReport(mojo);
if (localesRuntime != null && localesRuntime.length > 0) {
final Class<?> superclass = mojo.getClass().getSuperclass();
Mojo localeMojo = (Mojo)Flexmojos.invokePublicMethod(mojo, "clone");
final Method m = superclass.getDeclaredMethod("configureResourceBundle", String.class, superclass);
m.setAccessible(true);
String firstLocale = localesRuntime[0];
m.invoke(mojo, firstLocale, localeMojo);
//noinspection unchecked
((Map<String, String>)Flexmojos.invokePublicMethod(localeMojo, "getCache")).put("getProjectType", "rb.swc");
data.localeOutputFilepathPattern = Flexmojos.getOutput(localeMojo).replace(firstLocale, "{_locale_}");
// we don't release localeMojo (plexusContainer.release) - flexmojos doesn't do it too
}
}
finally {
plexusContainer.release(mojo);
}
}
finally {
session.setCurrentProject(oldProject);
if (flexmojosMojoExecution != null) {
maven.releaseMojoExecution(flexmojosMojoExecution);
}
}
}
private MavenSession createSession(MavenExecutionRequest request) throws ComponentLookupException {
final ThreadSafeMavenSession session = new ThreadSafeMavenSession(plexusContainer, createRepositorySession(request), request,
new DefaultMavenExecutionResult());
// flexmojos uses old LegacyRepositorySystem
plexusContainer.lookup(LegacySupport.class).setSession(session);
return session;
}
private RepositorySystemSession createRepositorySession(MavenExecutionRequest request) throws ComponentLookupException {
DefaultRepositorySystemSession session = (DefaultRepositorySystemSession)((DefaultMaven)plexusContainer.lookup(org.apache.maven.Maven.class)).newRepositorySession(request);
if (!request.isUpdateSnapshots()) {
session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_NEVER);
}
return session;
}
private MavenExecutionRequest createExecutionRequest(String[] args)
throws ComponentLookupException, SettingsBuildingException, MavenExecutionRequestPopulationException, IOException,
InvalidRepositoryException {
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setGlobalSettingsFile(new File(args[0]));
if (!args[1].equals(" ")) {
request.setUserSettingsFile(new File(args[1]));
}
request.setLocalRepository(plexusContainer.lookup(RepositorySystem.class).createLocalRepository(new File(args[2])));
Properties systemProperties = new Properties();
EnvironmentUtils.addEnvVars(systemProperties);
//noinspection UseOfPropertiesAsHashtable
systemProperties.putAll(System.getProperties());
request.setSystemProperties(systemProperties);
request.setOffline(args[3].equals("t")).setUpdateSnapshots(false).setCacheNotFound(true).setCacheTransferError(true);
plexusContainer.lookup(MavenExecutionRequestPopulator.class).populateFromSettings(request, createSettings(request));
// IDEA-76662
final List<String> activeProfiles = request.getActiveProfiles();
int profilesLength = in.readShort();
if (profilesLength > 0) {
while (profilesLength-- > 0) {
activeProfiles.add(in.readUTF());
}
}
request.setWorkspaceReader(new WorkspaceReaderImpl(in, this));
return request;
}
private Settings createSettings(MavenExecutionRequest mavenExecutionRequest) throws ComponentLookupException, SettingsBuildingException {
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setSystemProperties(request.getSystemProperties());
request.setGlobalSettingsFile(mavenExecutionRequest.getGlobalSettingsFile());
request.setUserSettingsFile(mavenExecutionRequest.getUserSettingsFile());
// IDEA-87004, getEffectiveSettings contains local repo as null, but our mavenExecutionRequest already has not-null local repo
Settings settings = plexusContainer.lookup(SettingsBuilder.class).build(request).getEffectiveSettings();
settings.setLocalRepository(mavenExecutionRequest.getLocalRepositoryPath().getPath());
return settings;
}
private static DefaultPlexusContainer createPlexusContainer() throws PlexusContainerException, ComponentLookupException {
ContainerConfiguration containerConfiguration = new DefaultContainerConfiguration()
.setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true)
.setClassWorld(new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader())).setName("maven");
final DefaultPlexusContainer container = new DefaultPlexusContainer(containerConfiguration);
final List<LocalRepositoryManagerFactory> factoryList = Collections.singletonList(container.lookup(LocalRepositoryManagerFactory.class, "simple"));
final String mavenVersion = container.lookup(RuntimeInformation.class).getMavenVersion();
// tracked impl is not suitable for us (our list of remote repo may be not equals - we don't want think about it)
if (mavenVersion.length() >= 5 && mavenVersion.charAt(2) == '0' && mavenVersion.charAt(4) < '4') {
final DefaultRepositorySystem repositorySystem = (DefaultRepositorySystem)container.lookup(RepositorySystem.class);
try {
repositorySystem.getClass().getMethod("setLocalRepositoryManagerFactories", List.class).invoke(repositorySystem, factoryList);
}
catch (Exception e) {
container.getLoggerManager().getLoggerForComponent(null).warn("", e);
}
}
else {
((DefaultLocalRepositoryProvider)container.lookup(LocalRepositoryProvider.class)).setLocalRepositoryManagerFactories(factoryList);
}
return container;
}
}