View Javadoc
1   /*
2    * Copyright 2019 SPF4J.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.spf4j.maven;
17  
18  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19  import java.io.BufferedInputStream;
20  import java.io.BufferedReader;
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.URI;
24  import java.nio.file.FileSystem;
25  import java.nio.file.FileSystemAlreadyExistsException;
26  import java.nio.file.FileSystems;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Properties;
32  import java.util.zip.ZipError;
33  import org.apache.avro.AvroRuntimeException;
34  import org.apache.avro.Schema;
35  import org.apache.avro.SchemaResolver;
36  import org.apache.avro.SchemaResolvers;
37  import org.eclipse.aether.RepositorySystem;
38  import org.eclipse.aether.RepositorySystemSession;
39  import org.eclipse.aether.repository.RemoteRepository;
40  import org.eclipse.aether.resolution.ArtifactResolutionException;
41  import org.spf4j.avro.SchemaRef;
42  import static org.spf4j.maven.MavenRepositoryUtils.getRepositorySystem;
43  import static org.spf4j.maven.MavenRepositoryUtils.getRepositorySystemSession;
44  
45  /**
46   * @author Zoltan Farkas
47   */
48  public final class MavenSchemaResolver implements SchemaResolver {
49  
50    private final RepositorySystem repoSystem;
51  
52    private final RepositorySystemSession repoSystemSession;
53  
54    private final List<RemoteRepository> remotes;
55  
56    private final String classifier;
57  
58    private final String extension;
59  
60    @SuppressFBWarnings("EI_EXPOSE_REP2")
61    public MavenSchemaResolver(final RepositorySystem repoSystem, final RepositorySystemSession repoSystemSession,
62            final List<RemoteRepository> remotes, final String classifier, final String extension) {
63      this.repoSystem = repoSystem;
64      this.repoSystemSession = repoSystemSession;
65      this.remotes = remotes;
66      this.classifier = classifier;
67      this.extension = extension;
68    }
69  
70    @SuppressFBWarnings("EI_EXPOSE_REP2")
71    public MavenSchemaResolver(final List<RemoteRepository> repos,
72            final File localRepo, final String classifier, final String extension) {
73      RepositorySystem repositorySystem = getRepositorySystem();
74      RepositorySystemSession repositorySystemSession = getRepositorySystemSession(repositorySystem, localRepo);
75      this.repoSystem = repositorySystem;
76      this.repoSystemSession = repositorySystemSession;
77      this.remotes = repos;
78      this.classifier = classifier;
79      this.extension = extension;
80    }
81  
82    @Override
83    @SuppressFBWarnings("PCAIL_POSSIBLE_CONSTANT_ALLOCATION_IN_LOOP")
84    public Schema resolveSchema(final String id) {
85      SchemaRef ref = new SchemaRef(id);
86      try {
87        File artifact = MavenRepositoryUtils.resolveArtifact(ref.getGroupId(), ref.getArtifactId(),
88                classifier, extension, ref.getVersion(), remotes, repoSystem, repoSystemSession);
89        URI zipUri = URI.create("jar:" + artifact.toURI().toURL());
90        FileSystem zipFs;
91        synchronized (zipUri.toString().intern()) { // newFileSystem fails if already one there...
92          try {
93            zipFs = FileSystems.newFileSystem(zipUri, Collections.emptyMap());
94          } catch (FileSystemAlreadyExistsException ex) {
95            zipFs = FileSystems.getFileSystem(zipUri);
96          } catch (ZipError ze) {
97            throw new AvroRuntimeException("Cannot resolve " + id, ze);
98          }
99        }
100       for (Path root : zipFs.getRootDirectories()) {
101         Path index = root.resolve("schema_index.properties");
102         if (Files.exists(index)) {
103           Properties prop = new Properties();
104           try (BufferedReader indexReader = Files.newBufferedReader(index)) {
105             prop.load(indexReader);
106           }
107           String schemaName = prop.getProperty(ref.getRef());
108           if (schemaName == null) {
109             throw new AvroRuntimeException("unable to resolve schema: " + id + " missing from index " + index);
110           }
111           Path schemaPath = root.resolve(schemaName.replace('.', '/') + ".avsc");
112           try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(schemaPath))) {
113             return new Schema.Parser().parse(bis);
114           }
115         }
116       }
117       throw new IOException("unable to resolve schema: " + id);
118 
119     } catch (ArtifactResolutionException | IOException ex) {
120       throw new AvroRuntimeException("Cannot resolve " + id, ex);
121     }
122   }
123 
124   @Override
125   public String getId(final Schema schema) {
126     return schema.getProp("mvnId");
127   }
128 
129   @Override
130   public void registerAsDefault() {
131     SchemaResolvers.registerDefault(this);
132   }
133 
134   @Override
135   public String toString() {
136     return "MavenSchemaResolver{" + "repoSystem=" + repoSystem + ", repoSystemSession=" + repoSystemSession
137             + ", remotes=" + remotes + ", classifier=" + classifier + ", extension=" + extension + '}';
138   }
139 
140 
141 
142 }