gnodet-bot commented on code in PR #13585: URL: https://github.com/apache/maven/pull/13585#discussion_r4298814212
########## impl/maven-cli/src/main/java/org/apache/maven/cling/event/ExecutionListenerAdapter.java: ########## @@ -0,0 +1,642 @@ +/* + * 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.cling.event; + +import java.lang.reflect.Proxy; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.apache.maven.api.DependencyCoordinates; +import org.apache.maven.api.ExecutionEventType; +import org.apache.maven.api.Language; +import org.apache.maven.api.MojoExecution; +import org.apache.maven.api.Packaging; +import org.apache.maven.api.Plugin; +import org.apache.maven.api.ProducedArtifact; +import org.apache.maven.api.Project; +import org.apache.maven.api.Session; +import org.apache.maven.api.Type; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.model.PluginContainer; +import org.apache.maven.api.model.Profile; +import org.apache.maven.execution.BuildFailure; +import org.apache.maven.execution.BuildSuccess; +import org.apache.maven.execution.BuildSummary; +import org.apache.maven.execution.ExecutionEvent; +import org.apache.maven.execution.ExecutionListener; +import org.apache.maven.execution.MavenExecutionResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.impl.DefaultMojoExecution; +import org.apache.maven.internal.impl.InternalMavenSession; +import org.apache.maven.project.MavenProject; + +/** + * Adapts a v4 {@link org.apache.maven.api.ExecutionListener} to the legacy + * v3 {@link org.apache.maven.execution.ExecutionListener} interface. + * + * @since 4.1.0 + */ +public class ExecutionListenerAdapter implements ExecutionListener { + + private final org.apache.maven.api.ExecutionListener delegate; + + public ExecutionListenerAdapter(org.apache.maven.api.ExecutionListener delegate) { + this.delegate = Objects.requireNonNull(delegate, "delegate cannot be null"); + } + + public org.apache.maven.api.ExecutionListener getDelegate() { + return delegate; + } + + @Override + public void projectDiscoveryStarted(ExecutionEvent event) { + delegate.projectDiscoveryStarted(toApiEvent(event, ExecutionEventType.PROJECT_DISCOVERY_STARTED)); + } + + @Override + public void sessionStarted(ExecutionEvent event) { + delegate.sessionStarted(toApiEvent(event, ExecutionEventType.SESSION_STARTED)); + } + + @Override + public void sessionEnded(ExecutionEvent event) { + syncSessionResult(event); + delegate.sessionEnded(toApiEvent(event, ExecutionEventType.SESSION_ENDED)); + } + + @Override + public void projectSkipped(ExecutionEvent event) { + syncProjectDuration(event); + delegate.projectSkipped(toApiEvent(event, ExecutionEventType.PROJECT_SKIPPED)); + } + + @Override + public void projectStarted(ExecutionEvent event) { + delegate.projectStarted(toApiEvent(event, ExecutionEventType.PROJECT_STARTED)); + } + + @Override + public void projectSucceeded(ExecutionEvent event) { + syncProjectDuration(event); + delegate.projectSucceeded(toApiEvent(event, ExecutionEventType.PROJECT_SUCCEEDED)); + } + + @Override + public void projectFailed(ExecutionEvent event) { + syncProjectDuration(event); + delegate.projectFailed(toApiEvent(event, ExecutionEventType.PROJECT_FAILED)); + } + + @Override + public void mojoSkipped(ExecutionEvent event) { + delegate.mojoSkipped(toApiEvent(event, ExecutionEventType.MOJO_SKIPPED)); + } + + @Override + public void mojoStarted(ExecutionEvent event) { + delegate.mojoStarted(toApiEvent(event, ExecutionEventType.MOJO_STARTED)); + } + + @Override + public void mojoSucceeded(ExecutionEvent event) { + delegate.mojoSucceeded(toApiEvent(event, ExecutionEventType.MOJO_SUCCEEDED)); + } + + @Override + public void mojoFailed(ExecutionEvent event) { + delegate.mojoFailed(toApiEvent(event, ExecutionEventType.MOJO_FAILED)); + } + + @Override + public void forkStarted(ExecutionEvent event) { + delegate.forkStarted(toApiEvent(event, ExecutionEventType.FORK_STARTED)); + } + + @Override + public void forkSucceeded(ExecutionEvent event) { + delegate.forkSucceeded(toApiEvent(event, ExecutionEventType.FORK_SUCCEEDED)); + } + + @Override + public void forkFailed(ExecutionEvent event) { + delegate.forkFailed(toApiEvent(event, ExecutionEventType.FORK_FAILED)); + } + + @Override + public void forkedProjectStarted(ExecutionEvent event) { + delegate.forkedProjectStarted(toApiEvent(event, ExecutionEventType.FORKED_PROJECT_STARTED)); + } + + @Override + public void forkedProjectSucceeded(ExecutionEvent event) { + delegate.forkedProjectSucceeded(toApiEvent(event, ExecutionEventType.FORKED_PROJECT_SUCCEEDED)); + } + + @Override + public void forkedProjectFailed(ExecutionEvent event) { + delegate.forkedProjectFailed(toApiEvent(event, ExecutionEventType.FORKED_PROJECT_FAILED)); + } + + protected org.apache.maven.api.ExecutionEvent toApiEvent(ExecutionEvent event, ExecutionEventType type) { + if (event == null) { + return null; + } + Session v4Session = null; + InternalMavenSession internalSession = null; + if (event.getSession() != null) { + try { + if (event.getSession().getSession() instanceof InternalMavenSession ims) { + internalSession = ims; + v4Session = ims; + } else if (event.getSession().getSession() != null) { + v4Session = event.getSession().getSession(); + } + } catch (Exception ignored) { + } + if (v4Session == null) { + v4Session = createSessionAdapter(event.getSession()); + } + } + + Project v4Project = null; + if (event.getProject() != null) { + if (internalSession != null) { + try { + v4Project = internalSession.getProject(event.getProject()); + } catch (Exception ignored) { + } + } + if (v4Project == null) { + v4Project = new ProjectAdapter(event.getProject()); + } + } + + MojoExecution v4Mojo = null; + if (event.getMojoExecution() != null) { + if (internalSession != null) { + try { + v4Mojo = new DefaultMojoExecution(internalSession, event.getMojoExecution()); + } catch (Exception ignored) { + } + } + if (v4Mojo == null) { + v4Mojo = createMojoExecutionAdapter(event.getMojoExecution()); + } + } + + Exception exception = event.getException(); + return new SimpleExecutionEvent(type, v4Session, v4Project, v4Mojo, exception); + } + + static class SimpleExecutionEvent implements org.apache.maven.api.ExecutionEvent { + private final ExecutionEventType type; + private final Session session; + private final Project project; + private final MojoExecution mojoExecution; + private final Exception exception; + + SimpleExecutionEvent( + ExecutionEventType type, + Session session, + Project project, + MojoExecution mojoExecution, + Exception exception) { + this.type = type; + this.session = session; + this.project = project; + this.mojoExecution = mojoExecution; + this.exception = exception; + } + + @Override + public ExecutionEventType type() { + return type; + } + + @Override + public Session session() { + return session; + } + + @Override + public Optional<Project> project() { + return Optional.ofNullable(project); + } + + @Override + public Optional<MojoExecution> mojoExecution() { + return Optional.ofNullable(mojoExecution); + } + + @Override + public Optional<Exception> exception() { + return Optional.ofNullable(exception); + } + } + + record SimplePackaging(String id) implements Packaging { + @Override + public Type type() { + return (Type) Proxy.newProxyInstance( + Type.class.getClassLoader(), new Class<?>[] {Type.class}, (proxy, method, args) -> { + String name = method.getName(); + if ("id".equals(name) || "getExtension".equals(name)) { + return id; + } + if ("getClassifier".equals(name)) { + return ""; + } + if ("getLanguage".equals(name)) { + return Language.NONE; + } + if ("getPathTypes".equals(name)) { + return Collections.emptySet(); + } + if ("isIncludesDependencies".equals(name)) { + return false; + } + return null; + }); + } + Review Comment: 🔵 **Low: `Type` proxy missing `hashCode`/`equals`/`toString` handlers** The `InvocationHandler` for the `Type` proxy falls through to `return null` for any unhandled method. Since `hashCode()` returns a primitive `int`, auto-unboxing `null` will throw a `NullPointerException` if any downstream code calls `packaging().type().hashCode()` — e.g. if `Type` is ever used as a map key or in a set. The session and mojo-execution proxies in the same class already handle these three `Object` methods; the `Type` proxy should too. ```suggestion @Override public Type type() { return (Type) Proxy.newProxyInstance( Type.class.getClassLoader(), new Class<?>[] {Type.class}, (proxy, method, args) -> { String name = method.getName(); if ("id".equals(name) || "getExtension".equals(name)) { return id; } if ("getClassifier".equals(name)) { return ""; } if ("getLanguage".equals(name)) { return Language.NONE; } if ("getPathTypes".equals(name)) { return Collections.emptySet(); } if ("isIncludesDependencies".equals(name)) { return false; } if ("toString".equals(name)) { return "Type[" + id + "]"; } if ("hashCode".equals(name)) { return id.hashCode(); } if ("equals".equals(name)) { return proxy == (args != null && args.length > 0 ? args[0] : null); } return null; }); } ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
