blob: a494e72e62ec52036b5300f255ae9893c62305b2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.maven.newproject;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.repository.MirrorSelector;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.apache.maven.wagon.ConnectionException;
import org.apache.maven.wagon.Wagon;
import org.apache.maven.wagon.WagonException;
import org.apache.maven.wagon.authentication.AuthenticationException;
import org.apache.maven.wagon.authentication.AuthenticationInfo;
import org.apache.maven.wagon.proxy.ProxyInfo;
import org.apache.maven.wagon.repository.Repository;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.FileUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.netbeans.modules.maven.api.archetype.Archetype;
import org.netbeans.modules.maven.api.archetype.ArchetypeProvider;
import org.netbeans.modules.maven.embedder.EmbedderFactory;
import org.netbeans.modules.maven.embedder.MavenEmbedder;
import org.netbeans.modules.maven.indexer.api.RepositoryInfo;
import org.netbeans.modules.maven.indexer.api.RepositoryPreferences;
import org.openide.modules.Places;
import org.openide.util.NbCollections;
import org.openide.util.Utilities;
import org.openide.util.lookup.ServiceProvider;
/**
* list of archetypes coming from an archetype catalog
* @author mkleint
*/
public abstract class CatalogRepoProvider implements ArchetypeProvider {
private static final String EL_ARCHETYPES = "archetypes"; //NOI18N
private static final String EL_ARCHETYPE = "archetype"; //NOI18N
private static final String EL_ARTIFACTID = "artifactId"; //NOI18N
private static final String EL_DESCRIPTION = "description";
private static final String EL_GROUPID = "groupId"; //NOI18N
private static final String EL_REPOSITORY = "repository"; //NOI18N
private static final String EL_VERSION = "version"; //NOI18N
private static final Logger LOG = Logger.getLogger(CatalogRepoProvider.class.getName());
protected CatalogRepoProvider() {}
protected abstract URL file() throws IOException;
protected abstract String repository();
public @Override List<Archetype> getArchetypes() {
try {
return getArchetypes(file(), repository());
} catch (IOException ex) {
LOG.log(Level.INFO, null, ex);
return Collections.emptyList();
}
}
static List<Archetype> getArchetypes(URL file, String repository) {
List<Archetype> toRet = new ArrayList<Archetype>();
try {
if (file == null) {
return toRet;
}
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(file);
Element root = doc.getRootElement();
Namespace ns = root.getNamespace(); // should be http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 but missing in older copies
Element list = root.getChild(EL_ARCHETYPES, ns);
if (list != null) {
for (Element el : NbCollections.checkedListByCopy(list.getChildren(EL_ARCHETYPE, ns), Element.class, true)) {
String grId = el.getChildText(EL_GROUPID, ns);
String artId = el.getChildText(EL_ARTIFACTID, ns);
String ver = el.getChildText(EL_VERSION, ns);
String repo = el.getChildText(EL_REPOSITORY, ns);
String desc = el.getChildText(EL_DESCRIPTION, ns);
Archetype archetype = new Archetype(false);
if (grId != null && artId != null && ver != null) {
archetype.setArtifactId(artId);
archetype.setGroupId(grId);
archetype.setVersion(ver);
if (repo != null) {
archetype.setRepository(repo);
} else {
archetype.setRepository(repository);
}
if (desc != null) {
archetype.setDescription(desc);
}
toRet.add(archetype);
}
}
}
} catch (IOException exc) {
LOG.log(Level.INFO, null, exc);
} catch (JDOMException exc) {
LOG.log(Level.INFO, null, exc);
}
return toRet;
}
@ServiceProvider(service=ArchetypeProvider.class, position=100)
public static class LocalCatalogRepoProvider extends CatalogRepoProvider {
@Override protected URL file() throws IOException {
File f = new File(RepositorySystem.userMavenConfigurationHome, "archetype-catalog.xml"); //NOI18N
return f.isFile() ? Utilities.toURI(f).toURL() : null;
}
@Override protected String repository() {
return RepositorySystem.DEFAULT_LOCAL_REPO_ID;
}
}
// one day period for updating downloaded/cached archetype catalogs
private static final long ARCHETYPE_TIMEOUT = 1000 * 60 * 60 * 24; // * 7;
@ServiceProvider(service=ArchetypeProvider.class, position=50) //has to come before the localCatalogProvider one..
public static class RemoteCatalogProvider implements ArchetypeProvider {
@Override
public List<Archetype> getArchetypes() {
File root = Places.getCacheSubdirectory("mavenarchetypes"); //NOI18N
ArrayList<Archetype> toRet = new ArrayList<Archetype>();
MavenEmbedder embedder = EmbedderFactory.getOnlineEmbedder();
SettingsDecryptionResult settings = embedder.lookupComponent(SettingsDecrypter.class).decrypt(new DefaultSettingsDecryptionRequest(embedder.getSettings()));
for (RepositoryInfo info : RepositoryPreferences.getInstance().getRepositoryInfos()) {
if (info.isRemoteDownloadable()) {
File catalog = new File(new File( root, info.getId()), "archetype-catalog.xml"); //NOI18N
boolean download = false;
if (!catalog.exists()) {
download = true;
} else {
long lastM = catalog.lastModified();
if (lastM == 0) {
download = true;
} else if (lastM - System.currentTimeMillis() > ARCHETYPE_TIMEOUT) {
download = true;
}
}
if (download) {
download(info.getId(), info.getRepositoryUrl(), catalog, settings, embedder);
}
if (catalog.exists()) {
try {
toRet.addAll(CatalogRepoProvider.getArchetypes(Utilities.toURI(catalog).toURL(), info.getRepositoryUrl()));
} catch (MalformedURLException ex) {
LOG.log(Level.INFO, null, ex);
}
}
}
}
return toRet;
}
private void download(String id, String url, File catalog, SettingsDecryptionResult settings, MavenEmbedder embedder) {
String baseurl = url;
String repoId = id;
try {
catalog.getParentFile().mkdirs();
AuthenticationInfo wagonAuth = null;
// //this comes from maven-compat
MirrorSelector mrri = embedder.lookupComponent(MirrorSelector.class);
// handle mirroring
ArtifactRepositoryLayout layout = (ArtifactRepositoryLayout) embedder.getPlexus().lookup(ArtifactRepositoryLayout.ROLE, "default"); //NOI18N
ArtifactRepositoryFactory arf = embedder.lookupComponent(ArtifactRepositoryFactory.class);
//what is the update policy?
ArtifactRepositoryPolicy policy = new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY, ArtifactRepositoryPolicy.UPDATE_POLICY_DAILY);
ArtifactRepository art = arf.createArtifactRepository(repoId, baseurl, layout, policy, policy);
Mirror mirr = mrri.getMirror(art, embedder.getSettings().getMirrors());
if (mirr != null) {
baseurl = mirr.getUrl();
repoId = mirr.getId();
}
for (Server server : settings.getServers()) {
if (repoId.equals(server.getId())) {
wagonAuth = new AuthenticationInfo();
wagonAuth.setUserName(server.getUsername());
wagonAuth.setPassword(server.getPassword());
wagonAuth.setPassphrase(server.getPassphrase());
wagonAuth.setPrivateKey(server.getPrivateKey());
break;
}
}
String protocol = URI.create(baseurl).getScheme();
ProxyInfo wagonProxy = null;
for (Proxy proxy : settings.getProxies()) {
if (proxy.isActive()) {
wagonProxy = new ProxyInfo();
wagonProxy.setHost(proxy.getHost());
wagonProxy.setPort(proxy.getPort());
wagonProxy.setNonProxyHosts(proxy.getNonProxyHosts());
wagonProxy.setUserName(proxy.getUsername());
wagonProxy.setPassword(proxy.getPassword());
wagonProxy.setType(protocol);
break;
}
}
Wagon wagon = embedder.getPlexus().lookup(Wagon.class, protocol);
assert wagon != null;
Repository repository = new Repository(repoId, baseurl);
try {
// when working in the context of Maven, the WagonManager is already
// populated with proxy information from the Maven environment
if (wagonAuth != null) {
if (wagonProxy != null) {
wagon.connect(repository, wagonAuth, wagonProxy);
} else {
wagon.connect(repository, wagonAuth);
}
} else {
if (wagonProxy != null) {
wagon.connect(repository, wagonProxy);
} else {
wagon.connect(repository);
}
}
File temp = File.createTempFile("maven", "catalog"); //NOI18N
try {
wagon.get("archetype-catalog.xml", temp); //NOI18N
//only overwrite the old file or create file if the content is there.
if (temp.exists() && temp.length() > 0) {
FileUtils.copyFile(temp, catalog);
temp.delete();
}
} finally {
wagon.disconnect();
}
} catch (AuthenticationException ex) {
String msg = "Authentication exception connecting to " + repository; //NOI18N
throw new IOException(msg, ex);
} catch (WagonException ex) {
String msg = "Wagon exception connecting to " + repository; //NOI18N
throw new IOException(msg, ex);
} finally {
try {
wagon.disconnect();
} catch (ConnectionException ex) {
String msg = "Wagon exception disconnecting from " + repository; //NOI18N
throw new IOException(msg, ex);
}
}
} catch (ComponentLookupException ex) {
LOG.log(Level.FINE, null, ex);
} catch (IOException io) {
LOG.log(Level.INFO, null, io);
}
}
}
}