mirror of https://github.com/apache/maven.git
[MNG-7607] Add M4 Transport API (#884)
Something simple to use and would reuse all the auth/proxy etc data from Maven. Intentionally super-trivial API. If something more "serious" needed, plugin should probably roll it's own solution. --- https://issues.apache.org/jira/browse/MNG-7607
This commit is contained in:
parent
c6ecff9923
commit
f70b0019cc
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.apache.maven.api.services;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import org.apache.maven.api.RemoteRepository;
|
||||
import org.apache.maven.api.annotations.Consumer;
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
|
||||
/**
|
||||
* Transport for specified remote repository (using provided remote repository base URI as root). Must be treated as a
|
||||
* resource, best in try-with-resource block.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@Experimental
|
||||
@Consumer
|
||||
public interface Transport extends Closeable {
|
||||
/**
|
||||
* GETs the source URI content into target (does not have to exist, or will be overwritten if exist). The
|
||||
* source MUST BE relative from the {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @return {@code true} if operation succeeded, {@code false} if source does not exist.
|
||||
* @throws RuntimeException If failed (and not due source not exists).
|
||||
*/
|
||||
boolean get(@Nonnull URI relativeSource, @Nonnull Path target);
|
||||
|
||||
/**
|
||||
* GETs the source URI content as byte array. The source MUST BE relative from the {@link RemoteRepository#getUrl()}
|
||||
* root.
|
||||
*
|
||||
* @return the byte array if operation succeeded, {@code null} if source does not exist.
|
||||
* @throws RuntimeException If failed (and not due source not exists).
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<byte[]> getBytes(@Nonnull URI relativeSource);
|
||||
|
||||
/**
|
||||
* GETs the source URI content as string. The source MUST BE relative from the {@link RemoteRepository#getUrl()}
|
||||
* root.
|
||||
*
|
||||
* @return the string if operation succeeded, {@code null} if source does not exist.
|
||||
* @throws RuntimeException If failed (and not due source not exists).
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<String> getString(@Nonnull URI relativeSource, @Nonnull Charset charset);
|
||||
|
||||
/**
|
||||
* GETs the source URI content as string using UTF8 charset. The source MUST BE relative from the
|
||||
* {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @return the string if operation succeeded, {@code null} if source does not exist.
|
||||
* @throws RuntimeException If failed (and not due source not exists).
|
||||
*/
|
||||
@Nonnull
|
||||
default Optional<String> getString(@Nonnull URI relativeSource) {
|
||||
return getString(relativeSource, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* PUTs the source file (must exist as file) to target URI. The target MUST BE relative from the
|
||||
* {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @throws RuntimeException If PUT fails for any reason.
|
||||
*/
|
||||
void put(@Nonnull Path source, @Nonnull URI relativeTarget);
|
||||
|
||||
/**
|
||||
* PUTs the source byte array to target URI. The target MUST BE relative from the
|
||||
* {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @throws RuntimeException If PUT fails for any reason.
|
||||
*/
|
||||
void putBytes(@Nonnull byte[] source, @Nonnull URI relativeTarget);
|
||||
|
||||
/**
|
||||
* PUTs the source string to target URI. The target MUST BE relative from the
|
||||
* {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @throws RuntimeException If PUT fails for any reason.
|
||||
*/
|
||||
void putString(@Nonnull String source, @Nonnull Charset charset, @Nonnull URI relativeTarget);
|
||||
|
||||
/**
|
||||
* PUTs the source string using UTF8 charset to target URI. The target MUST BE relative from the
|
||||
* {@link RemoteRepository#getUrl()} root.
|
||||
*
|
||||
* @throws RuntimeException If PUT fails for any reason.
|
||||
*/
|
||||
default void putString(@Nonnull String source, @Nonnull URI relativeTarget) {
|
||||
putString(source, StandardCharsets.UTF_8, relativeTarget);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.apache.maven.api.services;
|
||||
|
||||
import org.apache.maven.api.RemoteRepository;
|
||||
import org.apache.maven.api.Service;
|
||||
import org.apache.maven.api.Session;
|
||||
import org.apache.maven.api.annotations.Consumer;
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
|
||||
/**
|
||||
* Transporter provider is a service that provides somewhat trivial transport capabilities backed by Maven internals.
|
||||
* This API does not try to cover all the requirements out there, just the basic ones, and is intentionally simple.
|
||||
* If plugin or extension needs anything more complex feature wise (i.e. HTTP range support or alike) it should
|
||||
* probably roll its own.
|
||||
* <p>
|
||||
* This implementation is backed by Maven Resolver API, supported protocols and transport selection depends on it. If
|
||||
* resolver preference regarding transport is altered, it will affect this service as well.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@Experimental
|
||||
@Consumer
|
||||
public interface TransportProvider extends Service {
|
||||
/**
|
||||
* Provides new {@link Transport} instance for given {@link RemoteRepository}, if possible.
|
||||
*
|
||||
* @throws TransportProviderException if passed in remote repository has invalid remote URL or unsupported protocol.
|
||||
*/
|
||||
@Nonnull
|
||||
Transport transport(@Nonnull Session session, @Nonnull RemoteRepository repository);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.apache.maven.api.services;
|
||||
|
||||
import org.apache.maven.api.annotations.Consumer;
|
||||
import org.apache.maven.api.annotations.Experimental;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
@Experimental
|
||||
@Consumer
|
||||
public class TransportProviderException extends MavenException {
|
||||
public TransportProviderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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.apache.maven.internal.impl;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.maven.api.services.Transport;
|
||||
import org.eclipse.aether.spi.connector.transport.GetTask;
|
||||
import org.eclipse.aether.spi.connector.transport.PutTask;
|
||||
import org.eclipse.aether.spi.connector.transport.Transporter;
|
||||
|
||||
@Named
|
||||
@Singleton
|
||||
public class DefaultTransport implements Transport {
|
||||
private final URI baseURI;
|
||||
private final Transporter transporter;
|
||||
|
||||
public DefaultTransport(URI baseURI, Transporter transporter) {
|
||||
this.baseURI = requireNonNull(baseURI);
|
||||
this.transporter = requireNonNull(transporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean get(URI relativeSource, Path target) {
|
||||
requireNonNull(relativeSource, "relativeSource is null");
|
||||
requireNonNull(target, "target is null");
|
||||
if (relativeSource.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Supplied URI is not relative");
|
||||
}
|
||||
URI source = baseURI.resolve(relativeSource);
|
||||
if (!source.toASCIIString().startsWith(baseURI.toASCIIString())) {
|
||||
throw new IllegalArgumentException("Supplied relative URI escapes baseUrl");
|
||||
}
|
||||
GetTask getTask = new GetTask(source);
|
||||
getTask.setDataFile(target.toFile());
|
||||
try {
|
||||
transporter.get(getTask);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
if (Transporter.ERROR_NOT_FOUND != transporter.classify(e)) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<byte[]> getBytes(URI relativeSource) {
|
||||
try {
|
||||
Path tempPath = null;
|
||||
try {
|
||||
tempPath = Files.createTempFile("transport-get", "tmp");
|
||||
if (get(relativeSource, tempPath)) {
|
||||
// TODO: check file size and prevent OOM?
|
||||
return Optional.of(Files.readAllBytes(tempPath));
|
||||
}
|
||||
return Optional.empty();
|
||||
} finally {
|
||||
if (tempPath != null) {
|
||||
Files.deleteIfExists(tempPath);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getString(URI relativeSource, Charset charset) {
|
||||
requireNonNull(charset, "charset is null");
|
||||
Optional<byte[]> data = getBytes(relativeSource);
|
||||
return data.map(bytes -> new String(bytes, charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Path source, URI relativeTarget) {
|
||||
requireNonNull(source, "source is null");
|
||||
requireNonNull(relativeTarget, "relativeTarget is null");
|
||||
if (Files.isRegularFile(source)) {
|
||||
throw new IllegalArgumentException("source file does not exist or is not a file");
|
||||
}
|
||||
if (relativeTarget.isAbsolute()) {
|
||||
throw new IllegalArgumentException("Supplied URI is not relative");
|
||||
}
|
||||
URI target = baseURI.resolve(relativeTarget);
|
||||
if (!target.toASCIIString().startsWith(baseURI.toASCIIString())) {
|
||||
throw new IllegalArgumentException("Supplied relative URI escapes baseUrl");
|
||||
}
|
||||
|
||||
PutTask putTask = new PutTask(target);
|
||||
putTask.setDataFile(source.toFile());
|
||||
try {
|
||||
transporter.put(putTask);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBytes(byte[] source, URI relativeTarget) {
|
||||
requireNonNull(source, "source is null");
|
||||
try {
|
||||
Path tempPath = null;
|
||||
try {
|
||||
tempPath = Files.createTempFile("transport-get", "tmp");
|
||||
Files.write(tempPath, source);
|
||||
put(tempPath, relativeTarget);
|
||||
} finally {
|
||||
if (tempPath != null) {
|
||||
Files.deleteIfExists(tempPath);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(String source, Charset charset, URI relativeTarget) {
|
||||
requireNonNull(source, "source string is null");
|
||||
requireNonNull(charset, "charset is null");
|
||||
putBytes(source.getBytes(charset), relativeTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
transporter.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.apache.maven.internal.impl;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.maven.api.RemoteRepository;
|
||||
import org.apache.maven.api.Session;
|
||||
import org.apache.maven.api.services.Transport;
|
||||
import org.apache.maven.api.services.TransportProvider;
|
||||
import org.apache.maven.api.services.TransportProviderException;
|
||||
import org.eclipse.aether.spi.connector.transport.TransporterProvider;
|
||||
import org.eclipse.aether.transfer.NoTransporterException;
|
||||
|
||||
@Named
|
||||
@Singleton
|
||||
public class DefaultTransportProvider implements TransportProvider {
|
||||
private final org.eclipse.aether.spi.connector.transport.TransporterProvider transporterProvider;
|
||||
|
||||
@Inject
|
||||
public DefaultTransportProvider(TransporterProvider transporterProvider) {
|
||||
this.transporterProvider = requireNonNull(transporterProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport transport(Session session, RemoteRepository repository) {
|
||||
try {
|
||||
URI baseURI = new URI(repository.getUrl());
|
||||
return new DefaultTransport(
|
||||
baseURI,
|
||||
transporterProvider.newTransporter(
|
||||
((DefaultSession) session).getSession(),
|
||||
((DefaultRemoteRepository) repository).getRepository()));
|
||||
} catch (URISyntaxException e) {
|
||||
throw new TransportProviderException("Remote repository URL invalid", e);
|
||||
} catch (NoTransporterException e) {
|
||||
throw new TransportProviderException("Unsupported remote repository", e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue