X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2Fosgi%2FNormalizeGroup.java;fp=org.argeo.slc.repo%2Fsrc%2Forg%2Fargeo%2Fslc%2Frepo%2Fosgi%2FNormalizeGroup.java;h=8679c8ce8a4d5e5776587a6948bc92e5c1fd8165;hb=825d60c5348dbe3f5be25b0bccf7bdebfe694219;hp=0000000000000000000000000000000000000000;hpb=5e991fff5cba01858dcc5747a27e637325bc5c8e;p=gpl%2Fargeo-jcr.git diff --git a/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/NormalizeGroup.java b/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/NormalizeGroup.java new file mode 100644 index 0000000..8679c8c --- /dev/null +++ b/org.argeo.slc.repo/src/org/argeo/slc/repo/osgi/NormalizeGroup.java @@ -0,0 +1,437 @@ +package org.argeo.slc.repo.osgi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeSet; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.io.FilenameUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.jcr.JcrMonitor; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.SlcNames; +import org.argeo.slc.SlcTypes; +import org.argeo.slc.repo.ArtifactIndexer; +import org.argeo.slc.repo.RepoConstants; +import org.argeo.slc.repo.RepoUtils; +import org.argeo.slc.repo.maven.ArtifactIdComparator; +import org.argeo.slc.repo.maven.MavenConventionsUtils; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; + +/** + * Make sure that all JCR metadata and Maven metadata are consistent for this + * group of OSGi bundles. + * + * The job is now done via the various {@code NodeIndexer} of the + * WorkspaceManager. TODO import dependencies in the workspace. + */ +@Deprecated +public class NormalizeGroup implements Runnable, SlcNames { + private final static CmsLog log = CmsLog.getLog(NormalizeGroup.class); + + private Repository repository; + private String workspace; + private String groupId; + private Boolean overridePoms = false; + private String artifactBasePath = "/"; + private String version = null; + private String parentPomCoordinates; + + private List excludedSuffixes = new ArrayList(); + + private ArtifactIndexer artifactIndexer = new ArtifactIndexer(); + // private JarFileIndexer jarFileIndexer = new JarFileIndexer(); + + /** TODO make it more generic */ + private List systemPackages = OsgiProfile.PROFILE_JAVA_SE_1_6.getSystemPackages(); + + // indexes + private Map packagesToSymbolicNames = new HashMap(); + private Map symbolicNamesToNodes = new HashMap(); + + private Set binaries = new TreeSet(new ArtifactIdComparator()); + private Set sources = new TreeSet(new ArtifactIdComparator()); + + public void run() { + Session session = null; + try { + session = repository.login(workspace); + Node groupNode = session.getNode(MavenConventionsUtils.groupPath(artifactBasePath, groupId)); + processGroupNode(groupNode, null); + } catch (Exception e) { + throw new SlcException("Cannot normalize group " + groupId + " in " + workspace, e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + public static void processGroupNode(Node groupNode, String version, Boolean overridePoms, JcrMonitor monitor) + throws RepositoryException { + // TODO set artifactsBase based on group node + NormalizeGroup ng = new NormalizeGroup(); + String groupId = groupNode.getProperty(SlcNames.SLC_GROUP_BASE_ID).getString(); + ng.setGroupId(groupId); + ng.setVersion(version); + ng.setOverridePoms(overridePoms); + ng.processGroupNode(groupNode, monitor); + } + + protected void processGroupNode(Node groupNode, JcrMonitor monitor) throws RepositoryException { + if (monitor != null) + monitor.subTask("Group " + groupId); + Node allArtifactsHighestVersion = null; + Session session = groupNode.getSession(); + aBases: for (NodeIterator aBases = groupNode.getNodes(); aBases.hasNext();) { + Node aBase = aBases.nextNode(); + if (aBase.isNodeType(SlcTypes.SLC_ARTIFACT_BASE)) { + Node highestAVersion = null; + for (NodeIterator aVersions = aBase.getNodes(); aVersions.hasNext();) { + Node aVersion = aVersions.nextNode(); + if (aVersion.isNodeType(SlcTypes.SLC_ARTIFACT_VERSION_BASE)) { + if (highestAVersion == null) { + highestAVersion = aVersion; + if (allArtifactsHighestVersion == null) + allArtifactsHighestVersion = aVersion; + + // BS will fail if artifacts arrive in this order + // Name1 - V1, name2 - V3, V1 will remain the + // allArtifactsHighestVersion + // Fixed below + else { + Version currVersion = extractOsgiVersion(aVersion); + Version highestVersion = extractOsgiVersion(allArtifactsHighestVersion); + if (currVersion.compareTo(highestVersion) > 0) + allArtifactsHighestVersion = aVersion; + } + + } else { + Version currVersion = extractOsgiVersion(aVersion); + Version currentHighestVersion = extractOsgiVersion(highestAVersion); + if (currVersion.compareTo(currentHighestVersion) > 0) { + highestAVersion = aVersion; + } + if (currVersion.compareTo(extractOsgiVersion(allArtifactsHighestVersion)) > 0) { + allArtifactsHighestVersion = aVersion; + } + } + + } + + } + if (highestAVersion == null) + continue aBases; + for (NodeIterator files = highestAVersion.getNodes(); files.hasNext();) { + Node file = files.nextNode(); + if (file.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) { + preProcessBundleArtifact(file); + file.getSession().save(); + if (log.isDebugEnabled()) + log.debug("Pre-processed " + file.getName()); + } + + } + } + } + + // if version not set or empty, use the highest version + // useful when indexing a product maven repository where + // all artifacts have the same version for a given release + // => the version can then be left empty + if (version == null || version.trim().equals("")) + if (allArtifactsHighestVersion != null) + version = allArtifactsHighestVersion.getProperty(SLC_ARTIFACT_VERSION).getString(); + else + version = "0.0"; + // throw new SlcException("Group version " + version + // + " is empty."); + + int bundleCount = symbolicNamesToNodes.size(); + if (log.isDebugEnabled()) + log.debug("Indexed " + bundleCount + " bundles"); + + int count = 1; + for (Node bundleNode : symbolicNamesToNodes.values()) { + processBundleArtifact(bundleNode); + bundleNode.getSession().save(); + if (log.isDebugEnabled()) + log.debug(count + "/" + bundleCount + " Processed " + bundleNode.getName()); + count++; + } + + // indexes + Set indexes = new TreeSet(new ArtifactIdComparator()); + Artifact indexArtifact = writeIndex(session, RepoConstants.BINARIES_ARTIFACT_ID, binaries); + indexes.add(indexArtifact); + indexArtifact = writeIndex(session, RepoConstants.SOURCES_ARTIFACT_ID, sources); + indexes.add(indexArtifact); + // sdk + writeIndex(session, RepoConstants.SDK_ARTIFACT_ID, indexes); + if (monitor != null) + monitor.worked(1); + } + + private Version extractOsgiVersion(Node artifactVersion) throws RepositoryException { + String rawVersion = artifactVersion.getProperty(SLC_ARTIFACT_VERSION).getString(); + String cleanVersion = rawVersion.replace("-SNAPSHOT", ".SNAPSHOT"); + Version osgiVersion = null; + // log invalid version value to enable tracking them + try { + osgiVersion = new Version(cleanVersion); + } catch (IllegalArgumentException e) { + log.error("Version string " + cleanVersion + " is invalid "); + String twickedVersion = twickInvalidVersion(cleanVersion); + osgiVersion = new Version(twickedVersion); + log.error("Using " + twickedVersion + " instead"); + // throw e; + } + return osgiVersion; + } + + private String twickInvalidVersion(String tmpVersion) { + String[] tokens = tmpVersion.split("\\."); + if (tokens.length == 3 && tokens[2].lastIndexOf("-") > 0) { + String newSuffix = tokens[2].replaceFirst("-", "."); + tmpVersion = tmpVersion.replaceFirst(tokens[2], newSuffix); + } else if (tokens.length > 4) { + // FIXME manually remove other "." + StringTokenizer st = new StringTokenizer(tmpVersion, ".", true); + StringBuilder builder = new StringBuilder(); + // Major + builder.append(st.nextToken()).append(st.nextToken()); + // Minor + builder.append(st.nextToken()).append(st.nextToken()); + // Micro + builder.append(st.nextToken()).append(st.nextToken()); + // Qualifier + builder.append(st.nextToken()); + while (st.hasMoreTokens()) { + // consume delimiter + st.nextToken(); + if (st.hasMoreTokens()) + builder.append("-").append(st.nextToken()); + } + tmpVersion = builder.toString(); + } + return tmpVersion; + } + + private Artifact writeIndex(Session session, String artifactId, Set artifacts) + throws RepositoryException { + Artifact artifact = new DefaultArtifact(groupId, artifactId, "pom", version); + Artifact parentArtifact = parentPomCoordinates != null ? new DefaultArtifact(parentPomCoordinates) : null; + String pom = MavenConventionsUtils.artifactsAsDependencyPom(artifact, artifacts, parentArtifact); + Node node = RepoUtils.copyBytesAsArtifact(session.getNode(artifactBasePath), artifact, pom.getBytes()); + artifactIndexer.index(node); + + // TODO factorize + String pomSha = JcrUtils.checksumFile(node, "SHA-1"); + JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".sha1", pomSha.getBytes()); + String pomMd5 = JcrUtils.checksumFile(node, "MD5"); + JcrUtils.copyBytesAsFile(node.getParent(), node.getName() + ".md5", pomMd5.getBytes()); + session.save(); + return artifact; + } + + protected void preProcessBundleArtifact(Node bundleNode) throws RepositoryException { + + String symbolicName = JcrUtils.get(bundleNode, SLC_SYMBOLIC_NAME); + if (symbolicName.endsWith(".source")) { + // TODO make a shared node with classifier 'sources'? + String bundleName = RepoUtils.extractBundleNameFromSourceName(symbolicName); + for (String excludedSuffix : excludedSuffixes) { + if (bundleName.endsWith(excludedSuffix)) + return;// skip adding to sources + } + sources.add(RepoUtils.asArtifact(bundleNode)); + return; + } + + NodeIterator exportPackages = bundleNode.getNodes(SLC_ + Constants.EXPORT_PACKAGE); + while (exportPackages.hasNext()) { + Node exportPackage = exportPackages.nextNode(); + String pkg = JcrUtils.get(exportPackage, SLC_NAME); + packagesToSymbolicNames.put(pkg, symbolicName); + } + + symbolicNamesToNodes.put(symbolicName, bundleNode); + for (String excludedSuffix : excludedSuffixes) { + if (symbolicName.endsWith(excludedSuffix)) + return;// skip adding to binaries + } + binaries.add(RepoUtils.asArtifact(bundleNode)); + + if (bundleNode.getSession().hasPendingChanges()) + bundleNode.getSession().save(); + } + + protected void processBundleArtifact(Node bundleNode) throws RepositoryException { + Node artifactFolder = bundleNode.getParent(); + String baseName = FilenameUtils.getBaseName(bundleNode.getName()); + + // pom + String pomName = baseName + ".pom"; + if (artifactFolder.hasNode(pomName) && !overridePoms) + return;// skip + + String pom = generatePomForBundle(bundleNode); + Node pomNode = JcrUtils.copyBytesAsFile(artifactFolder, pomName, pom.getBytes()); + // checksum + String bundleSha = JcrUtils.checksumFile(bundleNode, "SHA-1"); + JcrUtils.copyBytesAsFile(artifactFolder, bundleNode.getName() + ".sha1", bundleSha.getBytes()); + String pomSha = JcrUtils.checksumFile(pomNode, "SHA-1"); + JcrUtils.copyBytesAsFile(artifactFolder, pomNode.getName() + ".sha1", pomSha.getBytes()); + } + + private String generatePomForBundle(Node n) throws RepositoryException { + String ownSymbolicName = JcrUtils.get(n, SLC_SYMBOLIC_NAME); + + StringBuffer p = new StringBuffer(); + + // XML header + p.append("\n"); + p.append( + "\n"); + p.append("4.0.0"); + + // Artifact + p.append("").append(JcrUtils.get(n, SLC_GROUP_ID)).append("\n"); + p.append("").append(JcrUtils.get(n, SLC_ARTIFACT_ID)).append("\n"); + p.append("").append(JcrUtils.get(n, SLC_ARTIFACT_VERSION)).append("\n"); + p.append("pom\n"); + if (n.hasProperty(SLC_ + Constants.BUNDLE_NAME)) + p.append("").append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_NAME)).append("\n"); + if (n.hasProperty(SLC_ + Constants.BUNDLE_DESCRIPTION)) + p.append("").append(JcrUtils.get(n, SLC_ + Constants.BUNDLE_DESCRIPTION)) + .append("\n"); + + // Dependencies + Set dependenciesSymbolicNames = new TreeSet(); + Set optionalSymbolicNames = new TreeSet(); + NodeIterator importPackages = n.getNodes(SLC_ + Constants.IMPORT_PACKAGE); + while (importPackages.hasNext()) { + Node importPackage = importPackages.nextNode(); + String pkg = JcrUtils.get(importPackage, SLC_NAME); + if (packagesToSymbolicNames.containsKey(pkg)) { + String dependencySymbolicName = packagesToSymbolicNames.get(pkg); + if (JcrUtils.check(importPackage, SLC_OPTIONAL)) + optionalSymbolicNames.add(dependencySymbolicName); + else + dependenciesSymbolicNames.add(dependencySymbolicName); + } else { + if (!JcrUtils.check(importPackage, SLC_OPTIONAL) && !systemPackages.contains(pkg)) + log.warn("No bundle found for pkg " + pkg); + } + } + + if (n.hasNode(SLC_ + Constants.FRAGMENT_HOST)) { + String fragmentHost = JcrUtils.get(n.getNode(SLC_ + Constants.FRAGMENT_HOST), SLC_SYMBOLIC_NAME); + dependenciesSymbolicNames.add(fragmentHost); + } + + // TODO require bundles + + List dependencyNodes = new ArrayList(); + for (String depSymbName : dependenciesSymbolicNames) { + if (depSymbName.equals(ownSymbolicName)) + continue;// skip self + + if (symbolicNamesToNodes.containsKey(depSymbName)) + dependencyNodes.add(symbolicNamesToNodes.get(depSymbName)); + else + log.warn("Could not find node for " + depSymbName); + } + List optionalDependencyNodes = new ArrayList(); + for (String depSymbName : optionalSymbolicNames) { + if (symbolicNamesToNodes.containsKey(depSymbName)) + optionalDependencyNodes.add(symbolicNamesToNodes.get(depSymbName)); + else + log.warn("Could not find node for " + depSymbName); + } + + p.append("\n"); + for (Node dependencyNode : dependencyNodes) { + p.append("\n"); + p.append("\t").append(JcrUtils.get(dependencyNode, SLC_GROUP_ID)).append("\n"); + p.append("\t").append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID)).append("\n"); + p.append("\n"); + } + + if (optionalDependencyNodes.size() > 0) + p.append("\n"); + for (Node dependencyNode : optionalDependencyNodes) { + p.append("\n"); + p.append("\t").append(JcrUtils.get(dependencyNode, SLC_GROUP_ID)).append("\n"); + p.append("\t").append(JcrUtils.get(dependencyNode, SLC_ARTIFACT_ID)).append("\n"); + p.append("\ttrue\n"); + p.append("\n"); + } + p.append("\n"); + + // Dependency management + p.append("\n"); + p.append("\n"); + p.append("\n"); + p.append("\t").append(groupId).append("\n"); + p.append("\t").append(ownSymbolicName.endsWith(".source") ? RepoConstants.SOURCES_ARTIFACT_ID + : RepoConstants.BINARIES_ARTIFACT_ID).append("\n"); + p.append("\t").append(version).append("\n"); + p.append("\tpom\n"); + p.append("\timport\n"); + p.append("\n"); + p.append("\n"); + p.append("\n"); + + p.append("\n"); + return p.toString(); + } + + /* DEPENDENCY INJECTION */ + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public void setParentPomCoordinates(String parentPomCoordinates) { + this.parentPomCoordinates = parentPomCoordinates; + } + + public void setArtifactBasePath(String artifactBasePath) { + this.artifactBasePath = artifactBasePath; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setExcludedSuffixes(List excludedSuffixes) { + this.excludedSuffixes = excludedSuffixes; + } + + public void setOverridePoms(Boolean overridePoms) { + this.overridePoms = overridePoms; + } + + public void setArtifactIndexer(ArtifactIndexer artifactIndexer) { + this.artifactIndexer = artifactIndexer; + } +}