Skip to content

Commit 1da8ddc

Browse files
committed
Reimplement base maven repository
Now implementations are now only mostly responsible for reporting groupd id’s and artifact id’s. This allows the GoogleMavenRepository implementation to work better.
1 parent eb94f86 commit 1da8ddc

13 files changed

Lines changed: 448 additions & 249 deletions

MavenNet.Tests/MavenNet.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<OutputType>Library</OutputType>
88
<RootNamespace>MavenNet.Tests</RootNamespace>
99
<AssemblyName>MavenNet.Tests</AssemblyName>
10-
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
10+
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
1111
</PropertyGroup>
1212
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1313
<DebugSymbols>true</DebugSymbols>

MavenNet.Tests/Test.cs

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,55 @@ public class Test
1010
{
1111
const string GPS_LOCAL_REPO = "/Users/redth/Library/Developer/Xamarin/android-sdk-macosx/extras/google/m2repository/";
1212
const string ANDROID_THINGS_URL_REPO = "https://google.bintray.com/androidthings";
13+
const string GOOGLE_MAVEN_REPO = "https://dl.google.com/dl/android/maven2/";
1314

1415
[Test()]
15-
public async Task Test_Load_Metadata_URL()
16+
public async Task Test_Refresh_URL()
1617
{
17-
var repo = MavenRepository.OpenUrl(ANDROID_THINGS_URL_REPO);
18-
await repo.LoadMetadataAsync();
18+
var repo = MavenRepository.FromUrl(ANDROID_THINGS_URL_REPO);
19+
await repo.Refresh();
1920

20-
Assert.IsTrue(repo.Metadata.Any());
21+
Assert.IsTrue(repo.Groups.Any());
2122
}
2223

2324
[Test]
24-
public async Task Test_Load_Project_Model_URL()
25+
public async Task Test_Project_URL()
2526
{
26-
var repo = MavenRepository.OpenUrl(ANDROID_THINGS_URL_REPO);
27-
await repo.LoadMetadataAsync();
27+
var repo = MavenRepository.FromUrl(ANDROID_THINGS_URL_REPO);
28+
await repo.Refresh();
2829

2930
var project = await repo.GetProjectAsync("com.google.android.things", "androidthings", "0.2-devpreview");
3031

3132
Assert.IsTrue(project != null);
3233
}
3334

34-
[Category ("LOCAL_ONLY")]
3535
[Test]
36-
public async Task Test_Load_Metadata_FILE()
36+
public async Task Test_GroupIds_Project_URL()
3737
{
38-
var repo = MavenRepository.OpenDirectory(GPS_LOCAL_REPO);
39-
await repo.LoadMetadataAsync();
38+
var repo = MavenRepository.FromUrl(ANDROID_THINGS_URL_REPO);
39+
await repo.Refresh("com.google.android.things");
4040

41-
Assert.IsTrue(repo.Metadata.Any());
41+
var project = await repo.GetProjectAsync("com.google.android.things", "androidthings", "0.2-devpreview");
42+
43+
Assert.IsTrue(project != null);
44+
}
45+
46+
[Category("LOCAL_ONLY")]
47+
[Test]
48+
public async Task Test_Refresh_FILE()
49+
{
50+
var repo = MavenRepository.FromDirectory(GPS_LOCAL_REPO);
51+
await repo.Refresh();
52+
53+
Assert.IsTrue(repo.Groups.Any());
4254
}
4355

4456
[Category("LOCAL_ONLY")]
4557
[Test]
46-
public async Task Test_Load_Project_Model_FILE()
58+
public async Task Test_Project_FILE()
4759
{
48-
var repo = MavenRepository.OpenDirectory(GPS_LOCAL_REPO);
49-
await repo.LoadMetadataAsync();
60+
var repo = MavenRepository.FromDirectory(GPS_LOCAL_REPO);
61+
await repo.Refresh();
5062

5163
var project = await repo.GetProjectAsync("com.google.android.gms", "play-services-basement", "10.2.0");
5264

@@ -55,21 +67,53 @@ public async Task Test_Load_Project_Model_FILE()
5567
Assert.IsTrue(project.Dependencies?.Any());
5668
}
5769

58-
5970
[Category("LOCAL_ONLY")]
6071
[Test]
61-
public async Task Test_Load_Project_Artifacts_FILE()
72+
public async Task Test_GroupIds_Project_FILE()
6273
{
63-
var repo = MavenRepository.OpenDirectory(GPS_LOCAL_REPO);
64-
await repo.LoadMetadataAsync();
74+
var repo = MavenRepository.FromDirectory(GPS_LOCAL_REPO);
75+
await repo.Refresh("com.google.android.gms");
6576

6677
var project = await repo.GetProjectAsync("com.google.android.gms", "play-services-basement", "10.2.0");
6778

6879
Assert.IsTrue(project != null);
6980

70-
var artifacts = await project.GetArtifactsAsync();
81+
Assert.IsTrue(project.Dependencies?.Any());
82+
}
7183

72-
Assert.IsTrue(artifacts.Any());
84+
[Test]
85+
public async Task Test_Refresh_GOOGLE()
86+
{
87+
var repo = MavenRepository.FromGoogle();
88+
await repo.Refresh();
89+
90+
Assert.IsTrue(repo.Groups.Any());
91+
}
92+
93+
[Test]
94+
public async Task Test_Project_GOOGLE()
95+
{
96+
var repo = MavenRepository.FromGoogle();
97+
await repo.Refresh();
98+
99+
var project = await repo.GetProjectAsync("com.google.android.gms", "play-services-basement", "10.2.0");
100+
101+
Assert.IsTrue(project != null);
102+
103+
Assert.IsTrue(project.Dependencies?.Any());
104+
}
105+
106+
[Test]
107+
public async Task Test_GroupIds_Project_GOOGLE()
108+
{
109+
var repo = MavenRepository.FromGoogle();
110+
await repo.Refresh("com.google.android.gms");
111+
112+
var project = await repo.GetProjectAsync("com.google.android.gms", "play-services-basement", "10.2.0");
113+
114+
Assert.IsTrue(project != null);
115+
116+
Assert.IsTrue(project.Dependencies?.Any());
73117
}
74118
}
75119
}

MavenNet/DirectoryMavenRepository.cs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace MavenNet
1212
{
1313

14-
public sealed class DirectoryMavenRepository : MavenRepository
14+
public sealed class DirectoryMavenRepository : FileBasedMavenRepository
1515
{
1616
public DirectoryMavenRepository(string directoryPath)
1717
{
@@ -23,28 +23,13 @@ public DirectoryMavenRepository(string directoryPath)
2323

2424
public string BasePath { get; private set; }
2525

26-
public override Task<Stream> LoadFileAsync(string path)
26+
protected override Task<Stream> OpenFileAsync(string path)
2727
{
28-
var fullPath = BasePath;
29-
30-
if (!string.IsNullOrWhiteSpace (path))
31-
fullPath = Path.Combine(BasePath, path);
28+
var fullPath = CombinePaths(BasePath, path);
3229

3330
return Task.FromResult((Stream)File.OpenRead(fullPath));
3431
}
3532

36-
public override Task<string> LoadTextFileAsync(string path)
37-
{
38-
var fullPath = BasePath;
39-
40-
if (!string.IsNullOrWhiteSpace(path))
41-
fullPath = Path.Combine(BasePath, path);
42-
43-
using (var sr = new StreamReader(File.OpenRead(fullPath)))
44-
return sr.ReadToEndAsync();
45-
}
46-
47-
4833
public override Task<IEnumerable<string>> GetFilesAsync (string path)
4934
{
5035
var fullPath = BasePath;
@@ -75,9 +60,14 @@ public override Task<IEnumerable<string>> GetDirectoriesAsync(string path)
7560
return Task.FromResult(dirInfo.GetDirectories().Select(f => f.Name));
7661
}
7762

78-
public override string CombinePaths(params string[] paths)
63+
protected override char PathSeparator
64+
{
65+
get { return Path.DirectorySeparatorChar; }
66+
}
67+
68+
protected override string CombinePaths(params string[] parts)
7969
{
80-
return Path.Combine(paths);
70+
return Path.Combine(parts);
8171
}
8272
}
8373

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Net.Http;
6+
using System.Text;
7+
using System.Text.RegularExpressions;
8+
using System.Threading.Tasks;
9+
using MavenNet.Models;
10+
11+
namespace MavenNet
12+
{
13+
public abstract class FileBasedMavenRepository : MavenRepository
14+
{
15+
public abstract Task<IEnumerable<string>> GetFilesAsync(string path);
16+
public abstract Task<IEnumerable<string>> GetDirectoriesAsync(string path);
17+
18+
protected override async Task<IEnumerable<string>> GetGroupIdsAsync()
19+
{
20+
var groupIds = new List<string>();
21+
22+
await recurseDir(string.Empty, groupIds).ConfigureAwait(false);
23+
24+
return groupIds;
25+
}
26+
27+
protected override async Task<IEnumerable<Artifact>> GetArtifactsAsync(string groupId)
28+
{
29+
var artifacts = new List<Artifact>();
30+
31+
var groupDir = CombinePaths(groupId.Split('.'));
32+
33+
var artifactDirs = await GetDirectoriesAsync(groupDir).ConfigureAwait(false);
34+
35+
foreach (var artifactDir in artifactDirs) {
36+
37+
var versions = await GetDirectoriesAsync(CombinePaths(groupDir, artifactDir)).ConfigureAwait(false);
38+
39+
if (!string.IsNullOrEmpty(artifactDir) && versions != null && versions.Any())
40+
artifacts.Add(new Artifact(artifactDir, groupId, versions.ToArray()));
41+
}
42+
43+
return artifacts;
44+
}
45+
46+
async Task recurseDir(string path, List<string> groupIds)
47+
{
48+
// If this path was a group, what would the id be calculated as.
49+
// We are recursing to look for the folder below the group folder, which will contain a maven-metadata.xml file
50+
// So the group will actually be the parent of this folder, so we calculate one level up
51+
var groupId = path.Replace('/', '.').Replace('\\', '.').Trim('.');
52+
if (groupId.Contains('.'))
53+
groupId = groupId.Substring(0, groupId.LastIndexOf('.'));
54+
55+
// See if this group was already detected and exit our recursion if so
56+
if (!string.IsNullOrEmpty(groupId) && groupIds.Any(gid => gid.Equals(groupId, StringComparison.OrdinalIgnoreCase)))
57+
return;
58+
59+
// If we got this far, we aren't trying to process a duplicate group name, so continue looking for maven-metadata.xml
60+
var files = await GetFilesAsync(path).ConfigureAwait(false);
61+
62+
// Look for maven-metadata.xml
63+
var metadataItem = files?.FirstOrDefault(f => f.Equals("maven-metadata.xml", StringComparison.OrdinalIgnoreCase));
64+
65+
// If we found the maven-metadata.xml file, we are on an artifact folder
66+
// We can stop recursing subdirs at this point since we found artifact info
67+
if (!string.IsNullOrEmpty (metadataItem))
68+
{
69+
groupIds.Add(groupId);
70+
}
71+
else
72+
{
73+
// If no maven-metadata.xml file was found let's try to keep recursing folders to find an artifact dir
74+
var dirs = await GetDirectoriesAsync(path).ConfigureAwait(false);
75+
76+
// Recurse over subdirs
77+
foreach (var dir in dirs)
78+
{
79+
var rPath = CombinePaths(path, dir);
80+
await recurseDir(rPath, groupIds).ConfigureAwait(false);
81+
}
82+
}
83+
}
84+
}
85+
86+
internal class DirectoryListing
87+
{
88+
public bool IsDirectory { get; set; }
89+
public string Name { get; set; }
90+
}
91+
}

MavenNet/GoogleMavenRepository.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Net.Http;
6+
using System.Text;
7+
using System.Text.RegularExpressions;
8+
using System.Threading.Tasks;
9+
using System.Xml.Linq;
10+
using System.Xml.XPath;
11+
using MavenNet.Models;
12+
13+
namespace MavenNet
14+
{
15+
16+
public sealed class GoogleMavenRepository : MavenRepository
17+
{
18+
HttpClient http;
19+
20+
public GoogleMavenRepository()
21+
{
22+
http = new HttpClient();
23+
BaseUri = new Uri("https://dl.google.com/dl/android/maven2/");
24+
}
25+
26+
public Uri BaseUri { get; private set; }
27+
28+
Dictionary<string, string> htmlListingCache = new Dictionary<string, string>();
29+
30+
31+
protected async override Task<IEnumerable<string>> GetGroupIdsAsync()
32+
{
33+
var uriBuilder = new UriBuilder(BaseUri);
34+
uriBuilder.Path += "master-index.xml";
35+
using (var hs = await http.GetStreamAsync(uriBuilder.Uri).ConfigureAwait(false))
36+
{
37+
var x = XDocument.Load(hs);
38+
return x.XPathSelectElements("/*/*")?.Select(a => a.Name.ToString()) ?? Enumerable.Empty<string>();
39+
}
40+
}
41+
42+
protected async override Task<IEnumerable<Artifact>> GetArtifactsAsync(string groupId)
43+
{
44+
var artifacts = new List<Artifact>();
45+
46+
var uriBuilder = new UriBuilder(BaseUri);
47+
uriBuilder.Path += CombinePaths(groupId.Split('.')) + "/group-index.xml";
48+
49+
using (var hs = await http.GetStreamAsync(uriBuilder.Uri).ConfigureAwait(false))
50+
{
51+
var x = XDocument.Load(hs);
52+
var artifactElems = x.XPathSelectElements("/*/*");
53+
54+
// Check to see if we matched any elements
55+
if (artifactElems != null && artifactElems.Any()) {
56+
foreach (var ae in artifactElems) {
57+
// Extract the id and versions
58+
var id = ae.Name.ToString();
59+
var versions = ae.Attribute("versions")?.Value?.Split(',') ?? new string[]{};
60+
61+
// Add them to our list if they are valid
62+
if (!string.IsNullOrEmpty(id) && versions != null && versions.Any())
63+
artifacts.Add(new Artifact(id, groupId, versions));
64+
}
65+
}
66+
}
67+
68+
return artifacts;
69+
}
70+
71+
protected override char PathSeparator
72+
{
73+
get { return '/'; }
74+
}
75+
76+
protected override string CombinePaths(params string[] parts)
77+
{
78+
return string.Join("/", parts).Trim('/');
79+
}
80+
81+
protected override Task<Stream> OpenFileAsync(string path)
82+
{
83+
var uriBuilder = new UriBuilder(BaseUri);
84+
uriBuilder.Path += path;
85+
86+
return http.GetStreamAsync(uriBuilder.Uri);
87+
}
88+
}
89+
}

MavenNet/IMavenRepository.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using MavenNet.Models;
4+
5+
namespace MavenNet
6+
{
7+
public interface IMavenRepository
8+
{
9+
Task<Project> GetProjectAsync(string groupId, string artifactId);
10+
Task<Project> GetProjectAsync(string groupId, string artifactId, string version);
11+
12+
IList<Group> Groups { get; }
13+
14+
Task Refresh();
15+
}
16+
}

0 commit comments

Comments
 (0)