/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed 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.jkiss.dbeaver.registry.maven; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.runtime.WebUtils; import org.jkiss.dbeaver.ui.TextUtils; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.dbeaver.utils.RuntimeUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; import org.jkiss.utils.StandardConstants; import org.jkiss.utils.xml.XMLException; import org.jkiss.utils.xml.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.io.*; import java.net.URISyntaxException; import java.net.URL; import java.util.*; /** * Maven artifact version descriptor (POM). */ public class MavenArtifactVersion implements IMavenIdentifier { private static final Log log = Log.getLog(MavenArtifactVersion.class); public static final String PROP_PROJECT_VERSION = "project.version"; public static final String PROP_PROJECT_GROUP_ID = "project.groupId"; public static final String PROP_PROJECT_ARTIFACT_ID = "project.artifactId"; private static final String DEFAULT_PROFILE_ID = "#root"; private MavenArtifact artifact; private String name; private String version; private String packaging; private String description; private String url; private MavenArtifactVersion parent; private List<MavenArtifactVersion> imports; private final List<MavenArtifactLicense> licenses = new ArrayList<>(); private final List<MavenProfile> profiles = new ArrayList<>(); private GeneralUtils.IVariableResolver propertyResolver = new GeneralUtils.IVariableResolver() { @Override public String get(String name) { switch (name) { case PROP_PROJECT_VERSION: return version; case PROP_PROJECT_GROUP_ID: return artifact.getGroupId(); case PROP_PROJECT_ARTIFACT_ID: return artifact.getArtifactId(); } for (MavenArtifactVersion v = MavenArtifactVersion.this; v != null; v = v.parent) { for (MavenProfile profile : v.profiles) { if (!profile.isActive()) { continue; } String value = profile.properties.get(name); if (value != null) { return value; } } } return null; } }; MavenArtifactVersion(@NotNull DBRProgressMonitor monitor, @NotNull MavenArtifact artifact, @NotNull String version) throws IOException { this.artifact = artifact; this.version = version; loadPOM(monitor); } @NotNull public MavenArtifact getArtifact() { return artifact; } public String getName() { return name; } @NotNull @Override public String getGroupId() { return artifact.getGroupId(); } @NotNull @Override public String getArtifactId() { return artifact.getArtifactId(); } @Nullable @Override public String getClassifier() { return artifact.getClassifier(); } @NotNull @Override public String getVersion() { return version; } @NotNull @Override public String getId() { return MavenArtifactReference.makeId(this); } @Nullable public String getPackaging() { return packaging; } public String getDescription() { return description; } public String getUrl() { return url; } public MavenArtifactVersion getParent() { return parent; } public List<MavenArtifactLicense> getLicenses() { return licenses; } public List<MavenProfile> getProfiles() { return profiles; } public List<MavenArtifactDependency> getDependencies() { List<MavenArtifactDependency> dependencies = new ArrayList<>(); for (MavenProfile profile : profiles) { if (profile.isActive() && !CommonUtils.isEmpty(profile.dependencies)) { dependencies.addAll(profile.dependencies); } } if (parent != null) { List<MavenArtifactDependency> parentDependencies = parent.getDependencies(); if (!CommonUtils.isEmpty(parentDependencies)) { dependencies.addAll(parentDependencies); } } return dependencies; } public File getCacheFile() { if (artifact.getRepository().getType() == MavenRepository.RepositoryType.LOCAL) { String externalURL = getExternalURL(MavenArtifact.FILE_JAR); try { return RuntimeUtils.getLocalFileFromURL(new URL(externalURL)); // return new File(new URL(externalURL).toURI()); } catch (Exception e) { log.warn("Bad repository URL", e); return new File(externalURL); } } return new File(artifact.getRepository().getLocalCacheDir(), artifact.getGroupId() + "/" + artifact.getVersionFileName(version, MavenArtifact.FILE_JAR)); } public String getExternalURL(String fileType) { return artifact.getFileURL(version, fileType); } public String getPath() { return artifact.toString() + ":" + version; } @Override public String toString() { return getPath(); } private File getLocalPOM() { if (artifact.getRepository().getType() == MavenRepository.RepositoryType.LOCAL) { try { return new File(GeneralUtils.makeURIFromFilePath(getRemotePOMLocation())); } catch (URISyntaxException e) { log.warn(e); } } return new File( artifact.getRepository().getLocalCacheDir(), artifact.getGroupId() + "/" + artifact.getVersionFileName(version, MavenArtifact.FILE_POM)); } private String getRemotePOMLocation() { return artifact.getFileURL(version, MavenArtifact.FILE_POM); } private void cachePOM(File localPOM) throws IOException { if (artifact.getRepository().getType() == MavenRepository.RepositoryType.LOCAL) { return; } String pomURL = getRemotePOMLocation(); try (InputStream is = WebUtils.openConnection(pomURL, artifact.getRepository().getAuthInfo()).getInputStream()) { File folder = localPOM.getParentFile(); if (!folder.exists() && !folder.mkdirs()) { throw new IOException("Can't create cache folder '" + folder.getAbsolutePath() + "'"); } try (OutputStream os = new FileOutputStream(localPOM)) { IOUtils.fastCopy(is, os); } } } private void loadPOM(DBRProgressMonitor monitor) throws IOException { monitor.subTask("Load POM " + this); File localPOM = getLocalPOM(); if (!localPOM.exists()) { cachePOM(localPOM); } Document pomDocument; try (InputStream mdStream = new FileInputStream(localPOM)) { pomDocument = XMLUtils.parseDocument(mdStream); } catch (XMLException e) { throw new IOException("Error parsing POM", e); } Element root = pomDocument.getDocumentElement(); name = XMLUtils.getChildElementBody(root, "name"); url = XMLUtils.getChildElementBody(root, "url"); version = XMLUtils.getChildElementBody(root, "version"); packaging = XMLUtils.getChildElementBody(root, "packaging"); description = XMLUtils.getChildElementBody(root, "description"); if (description != null) { description = TextUtils.compactWhiteSpaces(description.trim()); } { // Parent Element parentElement = XMLUtils.getChildElement(root, "parent"); if (parentElement != null) { String parentGroupId = XMLUtils.getChildElementBody(parentElement, "groupId"); String parentArtifactId = XMLUtils.getChildElementBody(parentElement, "artifactId"); String parentVersion = XMLUtils.getChildElementBody(parentElement, "version"); if (parentGroupId == null || parentArtifactId == null || parentVersion == null) { log.error("Broken parent reference: " + parentGroupId + ":" + parentArtifactId + ":" + parentVersion); } else { MavenArtifactReference parentReference = new MavenArtifactReference( parentGroupId, parentArtifactId, null, parentVersion); if (this.version == null) { this.version = parentReference.getVersion(); } parent = MavenRegistry.getInstance().findArtifact(monitor, this, parentReference); if (parent == null) { log.error("Artifact [" + this + "] parent [" + parentReference + "] not found"); } } } } { // Licenses Element licensesElement = XMLUtils.getChildElement(root, "licenses"); if (licensesElement != null) { for (Element prop : XMLUtils.getChildElementList(licensesElement, "license")) { licenses.add(new MavenArtifactLicense( XMLUtils.getChildElementBody(prop, "name"), XMLUtils.getChildElementBody(prop, "url") )); } } } // Default profile MavenProfile defaultProfile = new MavenProfile(DEFAULT_PROFILE_ID); defaultProfile.active = true; profiles.add(defaultProfile); parseProfile(monitor, defaultProfile, root); { // Profiles Element licensesElement = XMLUtils.getChildElement(root, "profiles"); if (licensesElement != null) { for (Element profElement : XMLUtils.getChildElementList(licensesElement, "profile")) { MavenProfile profile = new MavenProfile(XMLUtils.getChildElementBody(profElement, "id")); profiles.add(profile); parseProfile(monitor, profile, profElement); } } } monitor.worked(1); } private void parseProfile(DBRProgressMonitor monitor, MavenProfile profile, Element element) { { // Activation Element activationElement = XMLUtils.getChildElement(element, "activation"); if (activationElement != null) { String activeByDefault = XMLUtils.getChildElementBody(activationElement, "activeByDefault"); if (!CommonUtils.isEmpty(activeByDefault)) { profile.active = CommonUtils.getBoolean(activeByDefault); } String jdk = XMLUtils.getChildElementBody(activationElement, "jdk"); if (!CommonUtils.isEmpty(jdk)) { profile.active = MavenArtifact.versionMatches(System.getProperty(StandardConstants.ENV_JAVA_VERSION), jdk); } Element osElement = XMLUtils.getChildElement(activationElement, "os"); if (osElement != null) { } Element propElement = XMLUtils.getChildElement(activationElement, "property"); if (propElement != null) { String propName = XMLUtils.getChildElementBody(propElement, "name"); //String propValue = XMLUtils.getChildElementBody(propElement, "value"); // TODO: implement real properties checks. Now enable all profiles with !prop if (propName != null && propName.startsWith("!")) { profile.active = true; } } } } if (!profile.active) { // Do not parse dependencies of non-active profiles (most likely they will fail). return; } { // Properties Element propsElement = XMLUtils.getChildElement(element, "properties"); if (propsElement != null) { for (Element prop : XMLUtils.getChildElementList(propsElement)) { profile.properties.put(prop.getTagName(), XMLUtils.getElementBody(prop)); } } } { // Repositories Element repsElement = XMLUtils.getChildElement(element, "repositories"); if (repsElement != null) { for (Element repElement : XMLUtils.getChildElementList(repsElement, "repository")) { MavenRepository repository = new MavenRepository( XMLUtils.getChildElementBody(repElement, "id"), XMLUtils.getChildElementBody(repElement, "name"), XMLUtils.getChildElementBody(repElement, "url"), MavenRepository.RepositoryType.EXTERNAL); String layout = XMLUtils.getChildElementBody(repElement, "layout"); if ("legacy".equals(layout)) { log.debug("Skip legacy repository [" + repository + "]"); continue; } Element releasesElement = XMLUtils.getChildElement(repElement, "releases"); if (releasesElement == null) { continue; } boolean enabled = CommonUtils.toBoolean(XMLUtils.getChildElementBody(releasesElement, "enabled")); if (enabled) { profile.addRepository(repository); } } } } { // Dependencies Element dmElement = XMLUtils.getChildElement(element, "dependencyManagement"); if (dmElement != null) { profile.dependencyManagement = parseDependencies(monitor, dmElement, true); } profile.dependencies = parseDependencies(monitor, element, false); } } private List<MavenArtifactDependency> parseDependencies(DBRProgressMonitor monitor, Element element, boolean depManagement) { List<MavenArtifactDependency> result = new ArrayList<>(); Element dependenciesElement = XMLUtils.getChildElement(element, "dependencies"); if (dependenciesElement != null) { for (Element dep : XMLUtils.getChildElementList(dependenciesElement, "dependency")) { String groupId = evaluateString(XMLUtils.getChildElementBody(dep, "groupId")); String artifactId = evaluateString(XMLUtils.getChildElementBody(dep, "artifactId")); if (groupId == null || artifactId == null) { log.warn("Broken dependency reference: " + groupId + ":" + artifactId); continue; } String classifier = evaluateString(XMLUtils.getChildElementBody(dep, "classifier")); MavenArtifactDependency dmInfo = depManagement ? null : findDependencyManagement(groupId, artifactId); // Resolve scope MavenArtifactDependency.Scope scope = null; String scopeName = XMLUtils.getChildElementBody(dep, "scope"); if (!CommonUtils.isEmpty(scopeName)) { scope = MavenArtifactDependency.Scope.valueOf(scopeName.toUpperCase(Locale.ENGLISH)); } if (scope == null && dmInfo != null) { scope = dmInfo.getScope(); } if (scope == null) { scope = MavenArtifactDependency.Scope.COMPILE; } String optionalString = XMLUtils.getChildElementBody(dep, "optional"); boolean optional = optionalString == null ? (dmInfo != null && dmInfo.isOptional()) : CommonUtils.getBoolean(optionalString); // Resolve version String version = evaluateString(XMLUtils.getChildElementBody(dep, "version")); if (depManagement && scope == MavenArtifactDependency.Scope.IMPORT) { // Import another pom if (version == null) { log.error("Missing imported artifact [" + groupId + ":" + artifactId + "] version. Skip."); continue; } MavenArtifactReference importReference = new MavenArtifactReference( groupId, artifactId, classifier, version); MavenArtifactVersion importedVersion = MavenRegistry.getInstance().findArtifact(monitor, this, importReference); if (importedVersion == null) { log.error("Imported artifact [" + importReference + "] not found. Skip."); } if (imports == null) { imports = new ArrayList<>(); } imports.add(importedVersion); } else if (depManagement || (!optional && includesScope(scope))) { // TODO: maybe we should include optional or PROVIDED if (version == null && dmInfo != null) { version = dmInfo.getVersion(); } if (version == null) { log.error("Can't resolve artifact [" + groupId + ":" + artifactId + "] version. Skip."); continue; } MavenArtifactDependency dependency = new MavenArtifactDependency( groupId, artifactId, classifier, version, scope, optional); result.add(dependency); // Exclusions Element exclusionsElement = XMLUtils.getChildElement(dep, "exclusions"); if (exclusionsElement != null) { for (Element exclusion : XMLUtils.getChildElementList(exclusionsElement, "exclusion")) { dependency.addExclusion( new MavenArtifactReference( CommonUtils.notEmpty(XMLUtils.getChildElementBody(exclusion, "groupId")), CommonUtils.notEmpty(XMLUtils.getChildElementBody(exclusion, "artifactId")), null, "")); } } if (dmInfo != null) { List<MavenArtifactReference> dmExclusions = dmInfo.getExclusions(); if (dmExclusions != null) { for (MavenArtifactReference dmEx : dmExclusions) { dependency.addExclusion(dmEx); } } } } } } return result; } private boolean includesScope(MavenArtifactDependency.Scope scope) { return scope == MavenArtifactDependency.Scope.COMPILE || scope == MavenArtifactDependency.Scope.RUNTIME/* || scope == MavenArtifactDependency.Scope.PROVIDED*/; } private MavenArtifactDependency findDependencyManagement(String groupId, String artifactId) { for (MavenProfile profile : profiles) { if (profile.isActive() && profile.dependencyManagement != null) { for (MavenArtifactDependency dmArtifact : profile.dependencyManagement) { if (dmArtifact.getGroupId().equals(groupId) && dmArtifact.getArtifactId().equals(artifactId)) { return dmArtifact; } } } } // Check in imported BOMs if (imports != null) { for (MavenArtifactVersion i : imports) { MavenArtifactDependency dependencyManagement = i.findDependencyManagement(groupId, artifactId); if (dependencyManagement != null) { return dependencyManagement; } } } return parent == null ? null : parent.findDependencyManagement(groupId, artifactId); } private String evaluateString(String value) { if (value == null) { return null; } return GeneralUtils.replaceVariables(value, propertyResolver); } @NotNull public Collection<MavenRepository> getActiveRepositories() { Map<String, MavenRepository> repositories = new LinkedHashMap<>(); for (MavenArtifactVersion v = MavenArtifactVersion.this; v != null; v = v.parent) { for (MavenProfile profile : v.profiles) { if (profile.isActive()) { List<MavenRepository> pr = profile.getRepositories(); if (pr != null) { for (MavenRepository repository : pr) { repositories.put(repository.getId(), repository); } } } } } return repositories.values(); } }