diff --git a/.licenserc.yaml b/.licenserc.yaml index 2199c97a86f..4a1d226ff18 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -104,6 +104,8 @@ header: - 'dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/**' - 'dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/FormattingTuple.java' - 'dubbo-common/src/main/java/org/apache/dubbo/common/logger/helpers/MessageFormatter.java' + - 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java' + - 'dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java' comment: on-failure diff --git a/NOTICE b/NOTICE index b862c0f8336..6b0e80946f4 100644 --- a/NOTICE +++ b/NOTICE @@ -16,3 +16,23 @@ Copyright 2014 The Netty Project This product contains code form the t-digest Project: The code for the t-digest was originally authored by Ted Dunning Adrien Grand contributed the heart of the AVLTreeDigest (https://github.com/jpountz) + +This product contains the following code copied from Maven Protocol Buffers Plugin: +dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java + +Maven Protocol Buffers Plugin +============================= +Copyright (c) 2016 Maven Protocol Buffers Plugin Authors. All rights reserved. + +This product contains the following code copied from grpc-java-contrib: +dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java +Some portions of this file Copyright (c) 2019, Salesforce.com, Inc. and licensed under the BSD 3-Clause License + +grpc-java-contrib +==================== +Copyright (c) 2019, Salesforce.com, Inc. +All rights reserved. + + + + diff --git a/dubbo-compiler/README.md b/dubbo-compiler/README.md new file mode 100644 index 00000000000..f3e13e5a1ad --- /dev/null +++ b/dubbo-compiler/README.md @@ -0,0 +1,137 @@ +## dubbo-complier + +> dubbo-complier supports generating code based on .proto files + +### How to use + +#### 1.Define Proto file + +greeter.proto +```protobuf +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.dubbo.demo"; +option java_outer_classname = "DemoServiceProto"; +option objc_class_prefix = "DEMOSRV"; + +package demoservice; + +// The demo service definition. +service DemoService { + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +``` + +#### 2.Use dubbo-maven-plugin,rather than ```protobuf-maven-plugin``` + + now dubbo support his own protoc plugin base on dubbo-maven-plugin + +```xml + + org.apache.dubbo + dubbo-maven-plugin + 3.3.0 + + 3.3.0 + dubbo3 + protoc + com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} + + + +``` + +#### 3.generate file + +```java +/* +* 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.dubbo.demo; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +public final class DemoServiceDubbo { +private static final AtomicBoolean registered = new AtomicBoolean(); + +private static Class init() { +Class clazz = null; +try { +clazz = Class.forName(DemoServiceDubbo.class.getName()); +if (registered.compareAndSet(false, true)) { + org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( + org.apache.dubbo.demo.HelloReply.getDefaultInstance()); + org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( + org.apache.dubbo.demo.HelloRequest.getDefaultInstance()); +} +} catch (ClassNotFoundException e) { +// ignore +} +return clazz; +} + +private DemoServiceDubbo() {} + +public static final String SERVICE_NAME = "org.apache.dubbo.demo.DemoService"; + +/** +* Code generated for Dubbo +*/ +public interface IDemoService extends org.apache.dubbo.rpc.model.DubboStub { + +static Class clazz = init(); + + org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request); + + CompletableFuture sayHelloAsync(org.apache.dubbo.demo.HelloRequest request); + + +} + +} + +``` + +#### 4.others + +dubbo-maven-plugin protoc mojo supported configurations + +| configuration params | isRequired | explain | default | eg | +|:----------------------|------------|------------------------------------------------|------------------------------------------------------------|----------------------------------------------------------------------------| +| dubboVersion | true | dubbo version ,use for find Generator | ${dubbo.version} | 3.3.0 | +| dubboGenerateType | true | dubbo generator type | dubbo3 | grpc | +| protocExecutable | false | protoc executable,you can use local protoc.exe | | protoc | +| protocArtifact | false | download protoc from maven artifact | | com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier} | +| protoSourceDir | true | .proto files dir | ${basedir}/src/main/proto | ./proto | +| outputDir | true | generated file output dir | ${project.build.directory}/generated-sources/protobuf/java | ${basedir}/src/main/java | +| protocPluginDirectory | false | protoc plugin dir | ${project.build.directory}/protoc-plugins | ./target/protoc-plugins | + + +​ diff --git a/dubbo-compiler/pom.xml b/dubbo-compiler/pom.xml index 5cb8a9c8a7e..2d445e4a1ef 100644 --- a/dubbo-compiler/pom.xml +++ b/dubbo-compiler/pom.xml @@ -37,8 +37,8 @@ - com.salesforce.servicelibs - jprotoc + com.github.spullara.mustache.java + compiler io.grpc diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java index de9e67bd8ca..e2d387eae4f 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java @@ -16,6 +16,11 @@ */ package org.apache.dubbo.gen; +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.html.HtmlEscapers; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; @@ -23,22 +28,23 @@ import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location; -import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.Feature; import com.google.protobuf.compiler.PluginProtos; -import com.salesforce.jprotoc.Generator; -import com.salesforce.jprotoc.GeneratorException; -import com.salesforce.jprotoc.ProtoTypeMap; +import org.apache.dubbo.gen.utils.ProtoTypeMap; +import javax.annotation.Nonnull; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -public abstract class AbstractGenerator extends Generator { +public abstract class AbstractGenerator { + private static final MustacheFactory MUSTACHE_FACTORY = new DefaultMustacheFactory(); private static final int SERVICE_NUMBER_OF_PATHS = 2; private static final int METHOD_NUMBER_OF_PATHS = 4; @@ -58,10 +64,6 @@ protected String getInterfaceTemplateFileName() { return getClassPrefix() + getClassSuffix() + "InterfaceStub.mustache"; } - @Override - protected List supportedFeatures() { - return Collections.singletonList(Feature.FEATURE_PROTO3_OPTIONAL); - } private String getServiceJavaDocPrefix() { return " "; @@ -71,9 +73,7 @@ private String getMethodJavaDocPrefix() { return " "; } - @Override - public List generateFiles( - PluginProtos.CodeGeneratorRequest request) throws GeneratorException { + public List generateFiles(PluginProtos.CodeGeneratorRequest request) { final ProtoTypeMap typeMap = ProtoTypeMap.of(request.getProtoFileList()); List protosToGenerate = request.getProtoFileList().stream() @@ -85,12 +85,12 @@ public List generateFiles( } private List findServices(List protos, - ProtoTypeMap typeMap) { + ProtoTypeMap typeMap) { List contexts = new ArrayList<>(); protos.forEach(fileProto -> { for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount(); - serviceNumber++) { + serviceNumber++) { ServiceContext serviceContext = buildServiceContext( fileProto.getService(serviceNumber), typeMap, @@ -127,7 +127,7 @@ private String extractCommonPackageName(FileDescriptorProto proto) { } private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto, - ProtoTypeMap typeMap, List locations, int serviceNumber) { + ProtoTypeMap typeMap, List locations, int serviceNumber) { ServiceContext serviceContext = new ServiceContext(); serviceContext.fileName = getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java"; @@ -170,7 +170,7 @@ private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto, } private MethodContext buildMethodContext(MethodDescriptorProto methodProto, - ProtoTypeMap typeMap, List locations, int methodNumber) { + ProtoTypeMap typeMap, List locations, int methodNumber) { MethodContext methodContext = new MethodContext(); methodContext.originMethodName = methodProto.getName(); methodContext.methodName = lowerCaseFirst(methodProto.getName()); @@ -261,6 +261,20 @@ private List buildFile(ServiceContext c return files; } + protected String applyTemplate(@Nonnull String resourcePath, @Nonnull Object generatorContext) { + Preconditions.checkNotNull(resourcePath, "resourcePath"); + Preconditions.checkNotNull(generatorContext, "generatorContext"); + InputStream resource = MustacheFactory.class.getClassLoader().getResourceAsStream(resourcePath); + if (resource == null) { + throw new RuntimeException("Could not find resource " + resourcePath); + } else { + InputStreamReader resourceReader = new InputStreamReader(resource, Charsets.UTF_8); + Mustache template = MUSTACHE_FACTORY.compile(resourceReader, resourcePath); + return template.execute(new StringWriter(), generatorContext).toString(); + } + } + + private String absoluteDir(ServiceContext ctx) { return ctx.packageName.replace('.', '/'); } diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/DubboGeneratorPlugin.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/DubboGeneratorPlugin.java new file mode 100644 index 00000000000..70254505dcf --- /dev/null +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/DubboGeneratorPlugin.java @@ -0,0 +1,47 @@ +/* + * 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.dubbo.gen; + +import com.google.protobuf.compiler.PluginProtos; + +import java.io.IOException; +import java.util.List; + + +public class DubboGeneratorPlugin { + + public static void generate(AbstractGenerator generator) { + try{ + PluginProtos.CodeGeneratorRequest request = PluginProtos.CodeGeneratorRequest.parseFrom(System.in); + List files = generator.generateFiles(request); + PluginProtos.CodeGeneratorResponse.newBuilder().addAllFile(files).setSupportedFeatures(PluginProtos.CodeGeneratorResponse.Feature.FEATURE_PROTO3_OPTIONAL.getNumber()).build().writeTo(System.out); + }catch (Exception e){ + try { + PluginProtos.CodeGeneratorResponse.newBuilder().setError(e.getMessage()).build().writeTo(System.out); + } catch (IOException var6) { + exit(e); + } + }catch (Throwable var8) { + exit(var8); + } + } + + public static void exit(Throwable e){ + e.printStackTrace(System.err); + System.exit(1); + } +} diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/Dubbo3Generator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/Dubbo3Generator.java index 29a789b22e4..f10bec29e82 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/Dubbo3Generator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/Dubbo3Generator.java @@ -17,17 +17,12 @@ package org.apache.dubbo.gen.dubbo; import org.apache.dubbo.gen.AbstractGenerator; - -import com.salesforce.jprotoc.ProtocPlugin; +import org.apache.dubbo.gen.DubboGeneratorPlugin; public class Dubbo3Generator extends AbstractGenerator { public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new Dubbo3Generator()); - } else { - ProtocPlugin.debug(new Dubbo3Generator(), args[0]); - } + DubboGeneratorPlugin.generate(new Dubbo3Generator()); } @Override diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java index 1abf2a00641..8b20009a2aa 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/dubbo/DubboGenerator.java @@ -17,17 +17,13 @@ package org.apache.dubbo.gen.dubbo; import org.apache.dubbo.gen.AbstractGenerator; +import org.apache.dubbo.gen.DubboGeneratorPlugin; -import com.salesforce.jprotoc.ProtocPlugin; public class DubboGenerator extends AbstractGenerator { public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new DubboGenerator()); - } else { - ProtocPlugin.debug(new DubboGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new DubboGenerator()); } @Override diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java index dafdd5a6e65..e8608545c50 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/DubboGrpcGenerator.java @@ -17,17 +17,13 @@ package org.apache.dubbo.gen.grpc; import org.apache.dubbo.gen.AbstractGenerator; +import org.apache.dubbo.gen.DubboGeneratorPlugin; -import com.salesforce.jprotoc.ProtocPlugin; public class DubboGrpcGenerator extends AbstractGenerator { public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new DubboGrpcGenerator()); - } else { - ProtocPlugin.debug(new DubboGrpcGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new DubboGrpcGenerator()); } @Override diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java index dd9520b902a..ef80564ccd4 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/ReactorDubboGrpcGenerator.java @@ -17,8 +17,9 @@ package org.apache.dubbo.gen.grpc.reactive; import org.apache.dubbo.gen.AbstractGenerator; +import org.apache.dubbo.gen.DubboGeneratorPlugin; + -import com.salesforce.jprotoc.ProtocPlugin; public class ReactorDubboGrpcGenerator extends AbstractGenerator { @@ -33,10 +34,6 @@ protected String getClassSuffix() { } public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new ReactorDubboGrpcGenerator()); - } else { - ProtocPlugin.debug(new ReactorDubboGrpcGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new ReactorDubboGrpcGenerator()); } } diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java index 028332a27e5..e81e90f57dc 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/grpc/reactive/RxDubboGrpcGenerator.java @@ -17,8 +17,7 @@ package org.apache.dubbo.gen.grpc.reactive; import org.apache.dubbo.gen.AbstractGenerator; - -import com.salesforce.jprotoc.ProtocPlugin; +import org.apache.dubbo.gen.DubboGeneratorPlugin; public class RxDubboGrpcGenerator extends AbstractGenerator { @Override @@ -32,10 +31,6 @@ protected String getClassSuffix() { } public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new RxDubboGrpcGenerator()); - } else { - ProtocPlugin.debug(new RxDubboGrpcGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new RxDubboGrpcGenerator()); } } diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/Dubbo3TripleGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/Dubbo3TripleGenerator.java index ae3f8c0539e..9b08e2a5024 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/Dubbo3TripleGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/Dubbo3TripleGenerator.java @@ -17,17 +17,12 @@ package org.apache.dubbo.gen.tri; import org.apache.dubbo.gen.AbstractGenerator; - -import com.salesforce.jprotoc.ProtocPlugin; +import org.apache.dubbo.gen.DubboGeneratorPlugin; public class Dubbo3TripleGenerator extends AbstractGenerator { public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new Dubbo3TripleGenerator()); - } else { - ProtocPlugin.debug(new Dubbo3TripleGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new Dubbo3TripleGenerator()); } @Override diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java index dfc8d86e612..a5d2f55abc4 100644 --- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/tri/reactive/ReactorDubbo3TripleGenerator.java @@ -17,17 +17,13 @@ package org.apache.dubbo.gen.tri.reactive; -import com.salesforce.jprotoc.ProtocPlugin; import org.apache.dubbo.gen.AbstractGenerator; +import org.apache.dubbo.gen.DubboGeneratorPlugin; public class ReactorDubbo3TripleGenerator extends AbstractGenerator { public static void main(String[] args) { - if (args.length == 0) { - ProtocPlugin.generate(new ReactorDubbo3TripleGenerator()); - } else { - ProtocPlugin.debug(new ReactorDubbo3TripleGenerator(), args[0]); - } + DubboGeneratorPlugin.generate(new ReactorDubbo3TripleGenerator()); } @Override diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java new file mode 100644 index 00000000000..e725cf88a06 --- /dev/null +++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/utils/ProtoTypeMap.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019, Salesforce.com, Inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +package org.apache.dubbo.gen.utils; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.DescriptorProtos; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public final class ProtoTypeMap { + private static final Joiner DOT_JOINER = Joiner.on('.').skipNulls(); + private final ImmutableMap types; + + private ProtoTypeMap(@Nonnull ImmutableMap types) { + Preconditions.checkNotNull(types, "types"); + this.types = types; + } + + public static ProtoTypeMap of(@Nonnull Collection fileDescriptorProtos) { + Preconditions.checkNotNull(fileDescriptorProtos, "fileDescriptorProtos"); + Preconditions.checkArgument(!fileDescriptorProtos.isEmpty(), "fileDescriptorProtos.isEmpty()"); + ImmutableMap.Builder types = ImmutableMap.builder(); + + for (DescriptorProtos.FileDescriptorProto fileDescriptor : fileDescriptorProtos) { + DescriptorProtos.FileOptions fileOptions = fileDescriptor.getOptions(); + String protoPackage = fileDescriptor.hasPackage() ? "." + fileDescriptor.getPackage() : ""; + String javaPackage = Strings.emptyToNull(fileOptions.hasJavaPackage() ? fileOptions.getJavaPackage() : fileDescriptor.getPackage()); + String enclosingClassName = fileOptions.getJavaMultipleFiles() ? null : getJavaOuterClassname(fileDescriptor, fileOptions); + fileDescriptor.getEnumTypeList().forEach((e) -> { + types.put(protoPackage + "." + e.getName(), DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{e.getName()})); + }); + fileDescriptor.getMessageTypeList().forEach((m) -> { + recursivelyAddTypes(types, m, protoPackage, enclosingClassName, javaPackage); + }); + } + + return new ProtoTypeMap(types.build()); + } + + private static void recursivelyAddTypes(ImmutableMap.Builder types, DescriptorProtos.DescriptorProto m, String protoPackage, String enclosingClassName, String javaPackage) { + String protoTypeName = protoPackage + "." + m.getName(); + types.put(protoTypeName, DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{m.getName()})); + m.getEnumTypeList().forEach((e) -> { + types.put(protoPackage + "." + m.getName() + "." + e.getName(), DOT_JOINER.join(javaPackage, enclosingClassName, new Object[]{m.getName(), e.getName()})); + }); + m.getNestedTypeList().forEach((n) -> { + recursivelyAddTypes(types, n, protoPackage + "." + m.getName(), DOT_JOINER.join(enclosingClassName, m.getName(), new Object[0]), javaPackage); + }); + } + + public String toJavaTypeName(@Nonnull String protoTypeName) { + Preconditions.checkNotNull(protoTypeName, "protoTypeName"); + return (String)this.types.get(protoTypeName); + } + + private static String getJavaOuterClassname(DescriptorProtos.FileDescriptorProto fileDescriptor, DescriptorProtos.FileOptions fileOptions) { + if (fileOptions.hasJavaOuterClassname()) { + return fileOptions.getJavaOuterClassname(); + } else { + String filename = fileDescriptor.getName().substring(0, fileDescriptor.getName().length() - ".proto".length()); + if (filename.contains("/")) { + filename = filename.substring(filename.lastIndexOf(47) + 1); + } + + filename = makeInvalidCharactersUnderscores(filename); + filename = convertToCamelCase(filename); + filename = appendOuterClassSuffix(filename, fileDescriptor); + return filename; + } + } + + private static String appendOuterClassSuffix(String enclosingClassName, DescriptorProtos.FileDescriptorProto fd) { + return !fd.getEnumTypeList().stream().anyMatch((enumProto) -> { + return enumProto.getName().equals(enclosingClassName); + }) && !fd.getMessageTypeList().stream().anyMatch((messageProto) -> { + return messageProto.getName().equals(enclosingClassName); + }) && !fd.getServiceList().stream().anyMatch((serviceProto) -> { + return serviceProto.getName().equals(enclosingClassName); + }) ? enclosingClassName : enclosingClassName + "OuterClass"; + } + + private static String makeInvalidCharactersUnderscores(String filename) { + char[] filechars = filename.toCharArray(); + + for(int i = 0; i < filechars.length; ++i) { + char c = filechars[i]; + if (!CharMatcher.inRange('0', '9').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('a', 'z')).matches(c)) { + filechars[i] = '_'; + } + } + + return new String(filechars); + } + + private static String convertToCamelCase(String name) { + StringBuilder sb = new StringBuilder(); + sb.append(Character.toUpperCase(name.charAt(0))); + + for(int i = 1; i < name.length(); ++i) { + char c = name.charAt(i); + char prev = name.charAt(i - 1); + if (c != '_') { + if (prev != '_' && !CharMatcher.inRange('0', '9').matches(prev)) { + sb.append(c); + } else { + sb.append(Character.toUpperCase(c)); + } + } + } + + return sb.toString(); + } +} + diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index 89719f7cb68..07fca6d4d37 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -155,6 +155,7 @@ 1.58.0 0.8.1 1.2.2 + 0.9.10 1.7.36 1.2 @@ -889,6 +890,11 @@ jprotoc ${jprotoc_version} + + com.github.spullara.mustache.java + compiler + ${mustache_version} + io.fabric8 kubernetes-client diff --git a/dubbo-maven-plugin/pom.xml b/dubbo-maven-plugin/pom.xml index 0b3302d763d..bff3ce7aa64 100644 --- a/dubbo-maven-plugin/pom.xml +++ b/dubbo-maven-plugin/pom.xml @@ -70,6 +70,18 @@ commons-io 2.11.0 + + + org.sonatype.plexus + plexus-build-api + 0.0.7 + + + org.codehaus.plexus + plexus-utils + + + diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java new file mode 100644 index 00000000000..e2c88b6c7ef --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2019 Maven Protocol Buffers Plugin Authors. All rights reserved. + * + * Licensed 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.dubbo.maven.plugin.protoc; + +import org.apache.dubbo.maven.plugin.protoc.command.DefaultProtocCommandBuilder; +import org.apache.dubbo.maven.plugin.protoc.enums.DubboGenerateTypeEnum; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.ResolutionErrorHandler; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; +import org.apache.maven.repository.RepositorySystem; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.cli.CommandLineException; +import org.codehaus.plexus.util.cli.CommandLineUtils; +import org.codehaus.plexus.util.cli.Commandline; +import org.sonatype.plexus.build.incremental.BuildContext; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.Arrays; + +import static java.lang.String.format; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singleton; +import static org.codehaus.plexus.util.FileUtils.getFiles; + +@Mojo(name = "dubbo-protoc-compiler") +public class DubboProtocCompilerMojo extends AbstractMojo { + @Parameter(property = "protoSourceDir", defaultValue = "${basedir}/src/main/proto") + private File protoSourceDir; + @Parameter(property = "outputDir", defaultValue = "${project.build.directory}/generated-sources/protobuf/java") + private File outputDir; + @Parameter(required = true, property = "dubboVersion", defaultValue = "${dubbo.version}") + private String dubboVersion; + @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") + private List remoteRepositories; + @Parameter(required = false, property = "protocExecutable") + private String protocExecutable; + @Parameter(required = false, property = "protocArtifact") + private String protocArtifact; + @Parameter(required = false, defaultValue = "${project.build.directory}/protoc-plugins") + private File protocPluginDirectory; + @Parameter(required = true, property = "dubboGenerateType", defaultValue = "dubbo3") + private String dubboGenerateType; + @Parameter(defaultValue = "${project}", readonly = true) + protected MavenProject project; + @Parameter(defaultValue = "${session}", readonly = true) + protected MavenSession session; + @Parameter(required = true, readonly = true, property = "localRepository") + private ArtifactRepository localRepository; + @Component + private ArtifactFactory artifactFactory; + @Component + private RepositorySystem repositorySystem; + @Component + private ResolutionErrorHandler resolutionErrorHandler; + @Component + protected MavenProjectHelper projectHelper; + @Component + protected BuildContext buildContext; + final CommandLineUtils.StringStreamConsumer output = new CommandLineUtils.StringStreamConsumer(); + final CommandLineUtils.StringStreamConsumer error = new CommandLineUtils.StringStreamConsumer(); + private final DefaultProtocCommandBuilder defaultProtocCommandBuilder = new DefaultProtocCommandBuilder(); + private final DubboProtocPluginWrapperFactory dubboProtocPluginWrapperFactory = new DubboProtocPluginWrapperFactory(); + + public void execute() throws MojoExecutionException, MojoFailureException { + if (protocExecutable == null && protocArtifact != null) { + final Artifact artifact = createProtocArtifact(protocArtifact); + final File file = resolveBinaryArtifact(artifact); + protocExecutable = file.getAbsolutePath(); + } + if (protocExecutable == null) { + getLog().warn("No 'protocExecutable' parameter is configured, using the default: 'protoc'"); + protocExecutable = "protoc"; + } + getLog().info("using protocExecutable: " + protocExecutable); + DubboProtocPlugin dubboProtocPlugin = buildDubboProtocPlugin(dubboVersion, dubboGenerateType, protocPluginDirectory); + getLog().info("build dubbo protoc plugin:" + dubboProtocPlugin + " success"); + List commandArgs = defaultProtocCommandBuilder.buildProtocCommandArgs(new ProtocMetaData( + protocExecutable, protoSourceDir, findAllProtoFiles(protoSourceDir), outputDir, dubboProtocPlugin + )); + if (!outputDir.exists()) { + FileUtils.mkdir(outputDir.getAbsolutePath()); + } + try { + int exitStatus = executeCommandLine(commandArgs); + getLog().info("execute commandLine finished with exit code: " + exitStatus); + if (exitStatus != 0) { + getLog().error("PROTOC FAILED: " + getError()); + throw new MojoFailureException( + "protoc did not exit cleanly. Review output for more information."); + } else if (StringUtils.isNotBlank(getError())) { + getLog().warn("PROTOC: " + getError()); + } else { + linkProtoFilesToMaven(); + } + } catch (CommandLineException e) { + throw new MojoExecutionException(e); + } + } + + public void linkProtoFilesToMaven() { + linkProtoSources(); + linkGeneratedFiles(); + } + + public void linkProtoSources() { + projectHelper.addResource(project, protoSourceDir.getAbsolutePath(), + Collections.singletonList("**/*.proto*"), Collections.singletonList("")); + } + + public void linkGeneratedFiles() { + project.addCompileSourceRoot(outputDir.getAbsolutePath()); + buildContext.refresh(outputDir); + } + + public List findAllProtoFiles(final File protoSourceDir) { + if (protoSourceDir == null) { + throw new RuntimeException("'protoSourceDir' is null"); + } + if (!protoSourceDir.isDirectory()) { + throw new RuntimeException(format("%s is not a directory", protoSourceDir)); + } + final List protoFilesInDirectory; + try { + protoFilesInDirectory = getFiles(protoSourceDir, "**/*.proto*", ""); + } catch (IOException e) { + throw new RuntimeException("Unable to retrieve the list of files: " + e.getMessage(), e); + } + getLog().info("protoFilesInDirectory: " + protoFilesInDirectory); + return protoFilesInDirectory; + } + + public int executeCommandLine(List commandArgs) throws CommandLineException { + final Commandline cl = new Commandline(); + cl.setExecutable(protocExecutable); + cl.addArguments(commandArgs.toArray(new String[]{})); + int attemptsLeft = 3; + while (true) { + try { + getLog().info("commandLine:" + cl.toString()); + return CommandLineUtils.executeCommandLine(cl, null, output, error); + } catch (CommandLineException e) { + if (--attemptsLeft == 0 || e.getCause() == null) { + throw e; + } + getLog().warn("[PROTOC] Unable to invoke protoc, will retry " + attemptsLeft + " time(s)", e); + try { + Thread.sleep(1000L); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ex); + } + } + } + } + + private DubboProtocPlugin buildDubboProtocPlugin(String dubboVersion, String dubboGenerateType, File protocPluginDirectory) { + DubboProtocPlugin dubboProtocPlugin = new DubboProtocPlugin(); + DubboGenerateTypeEnum dubboGenerateTypeEnum = DubboGenerateTypeEnum.getByType(dubboGenerateType); + if (dubboGenerateTypeEnum == null) { + throw new RuntimeException(" can not find the dubboGenerateType: " + dubboGenerateType + ",please check it !"); + } + dubboProtocPlugin.setId(dubboGenerateType); + dubboProtocPlugin.setMainClass(dubboGenerateTypeEnum.getMainClass()); + dubboProtocPlugin.setDubboVersion(dubboVersion); + dubboProtocPlugin.setPluginDirectory(protocPluginDirectory); + dubboProtocPlugin.setJavaHome(System.getProperty("java.home")); + DubboProtocPluginWrapper protocPluginWrapper = dubboProtocPluginWrapperFactory.findByOs(); + dubboProtocPlugin.setResolvedJars(resolvePluginDependencies()); + File protocPlugin = protocPluginWrapper.createProtocPlugin(dubboProtocPlugin, getLog()); + boolean debugEnabled = getLog().isDebugEnabled(); + if (debugEnabled) { + getLog().debug("protocPlugin: " + protocPlugin.getAbsolutePath()); + } + dubboProtocPlugin.setProtocPlugin(protocPlugin); + return dubboProtocPlugin; + } + + private List resolvePluginDependencies() { + List resolvedJars = new ArrayList<>(); + final VersionRange versionSpec; + try { + versionSpec = VersionRange.createFromVersionSpec(dubboVersion); + } catch (InvalidVersionSpecificationException e) { + throw new RuntimeException("Invalid plugin version specification", e); + } + final Artifact protocPluginArtifact = + artifactFactory.createDependencyArtifact( + "org.apache.dubbo", + "dubbo-compiler", + versionSpec, + "jar", + "", + Artifact.SCOPE_RUNTIME); + final ArtifactResolutionRequest request = new ArtifactResolutionRequest() + .setArtifact(project.getArtifact()) + .setResolveRoot(false) + .setArtifactDependencies(Collections.singleton(protocPluginArtifact)) + .setManagedVersionMap(emptyMap()) + .setLocalRepository(localRepository) + .setRemoteRepositories(remoteRepositories) + .setOffline(session.isOffline()) + .setForceUpdate(session.getRequest().isUpdateSnapshots()) + .setServers(session.getRequest().getServers()) + .setMirrors(session.getRequest().getMirrors()) + .setProxies(session.getRequest().getProxies()); + + final ArtifactResolutionResult result = repositorySystem.resolve(request); + + try { + resolutionErrorHandler.throwErrors(request, result); + } catch (ArtifactResolutionException e) { + throw new RuntimeException("Unable to resolve plugin artifact: " + e.getMessage(), e); + } + + final Set artifacts = result.getArtifacts(); + + if (artifacts == null || artifacts.isEmpty()) { + throw new RuntimeException("Unable to resolve plugin artifact"); + } + + for (final Artifact artifact : artifacts) { + resolvedJars.add(artifact.getFile()); + } + + if (getLog().isDebugEnabled()) { + getLog().debug("Resolved jars: " + resolvedJars); + } + return resolvedJars; + } + + protected Artifact createProtocArtifact(final String artifactSpec) { + final String[] parts = artifactSpec.split(":"); + if (parts.length < 3 || parts.length > 5) { + throw new RuntimeException( + "Invalid artifact specification format" + + ", expected: groupId:artifactId:version[:type[:classifier]]" + + ", actual: " + artifactSpec); + } + final String type = parts.length >= 4 ? parts[3] : "exe"; + final String classifier = parts.length == 5 ? parts[4] : null; + //parts: [com.google.protobuf, protoc, 3.6.0, exe, osx-x86_64] + getLog().info("parts: " + Arrays.toString(parts)); + return createDependencyArtifact(parts[0], parts[1], parts[2], type, classifier); + } + + protected Artifact createDependencyArtifact( + final String groupId, + final String artifactId, + final String version, + final String type, + final String classifier + ) { + final VersionRange versionSpec; + try { + versionSpec = VersionRange.createFromVersionSpec(version); + } catch (final InvalidVersionSpecificationException e) { + throw new RuntimeException("Invalid version specification", e); + } + return artifactFactory.createDependencyArtifact( + groupId, + artifactId, + versionSpec, + type, + classifier, + Artifact.SCOPE_RUNTIME); + } + + protected File resolveBinaryArtifact(final Artifact artifact) { + final ArtifactResolutionResult result; + final ArtifactResolutionRequest request = new ArtifactResolutionRequest() + .setArtifact(project.getArtifact()) + .setResolveRoot(false) + .setResolveTransitively(false) + .setArtifactDependencies(singleton(artifact)) + .setManagedVersionMap(emptyMap()) + .setLocalRepository(localRepository) + .setRemoteRepositories(remoteRepositories) + .setOffline(session.isOffline()) + .setForceUpdate(session.getRequest().isUpdateSnapshots()) + .setServers(session.getRequest().getServers()) + .setMirrors(session.getRequest().getMirrors()) + .setProxies(session.getRequest().getProxies()); + result = repositorySystem.resolve(request); + try { + resolutionErrorHandler.throwErrors(request, result); + } catch (final ArtifactResolutionException e) { + throw new RuntimeException("Unable to resolve artifact: " + e.getMessage(), e); + } + + final Set artifacts = result.getArtifacts(); + + if (artifacts == null || artifacts.isEmpty()) { + throw new RuntimeException("Unable to resolve artifact"); + } + + final Artifact resolvedBinaryArtifact = artifacts.iterator().next(); + if (getLog().isDebugEnabled()) { + getLog().debug("Resolved artifact: " + resolvedBinaryArtifact); + } + + final File sourceFile = resolvedBinaryArtifact.getFile(); + final String sourceFileName = sourceFile.getName(); + final String targetFileName; + if (Os.isFamily(Os.FAMILY_WINDOWS) && !sourceFileName.endsWith(".exe")) { + targetFileName = sourceFileName + ".exe"; + } else { + targetFileName = sourceFileName; + } + final File targetFile = new File(protocPluginDirectory, targetFileName); + if (targetFile.exists()) { + getLog().debug("Executable file already exists: " + targetFile.getAbsolutePath()); + return targetFile; + } + try { + FileUtils.forceMkdir(protocPluginDirectory); + } catch (final IOException e) { + throw new RuntimeException("Unable to create directory " + protocPluginDirectory, e); + } + try { + FileUtils.copyFile(sourceFile, targetFile); + } catch (final IOException e) { + throw new RuntimeException("Unable to copy the file to " + protocPluginDirectory, e); + } + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + boolean b = targetFile.setExecutable(true); + if (!b) { + throw new RuntimeException("Unable to make executable: " + targetFile.getAbsolutePath()); + } + } + + if (getLog().isDebugEnabled()) { + getLog().debug("Executable file: " + targetFile.getAbsolutePath()); + } + return targetFile; + } + + + public String getError() { + return fixUnicodeOutput(error.getOutput()); + } + + public String getOutput() { + return fixUnicodeOutput(output.getOutput()); + } + + private static String fixUnicodeOutput(final String message) { + return new String(message.getBytes(), StandardCharsets.UTF_8); + } +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPlugin.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPlugin.java new file mode 100644 index 00000000000..630390bc18a --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPlugin.java @@ -0,0 +1,140 @@ +/* + * 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.dubbo.maven.plugin.protoc; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class DubboProtocPlugin { + + private String id; + private String mainClass; + private String dubboVersion; + private String javaHome; + private File pluginDirectory; + private List resolvedJars = new ArrayList<>(); + private List args = new ArrayList<>(); + private List jvmArgs = new ArrayList<>(); + private File protocPlugin = null; + + public DubboProtocPlugin() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getMainClass() { + return mainClass; + } + + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + } + + public String getDubboVersion() { + return dubboVersion; + } + + public void setDubboVersion(String dubboVersion) { + this.dubboVersion = dubboVersion; + } + + public String getJavaHome() { + return javaHome; + } + + public void setJavaHome(String javaHome) { + this.javaHome = javaHome; + } + + public File getPluginDirectory() { + return pluginDirectory; + } + + public void setPluginDirectory(File pluginDirectory) { + this.pluginDirectory = pluginDirectory; + } + + public List getResolvedJars() { + return resolvedJars; + } + + public void setResolvedJars(List resolvedJars) { + this.resolvedJars = resolvedJars; + } + + public void addResolvedJar(File jar) { + resolvedJars.add(jar); + } + + public List getArgs() { + return args; + } + + public void setArgs(List args) { + this.args = args; + } + + public void addArg(String arg) { + args.add(arg); + } + + public List getJvmArgs() { + return jvmArgs; + } + + public void setJvmArgs(List jvmArgs) { + this.jvmArgs = jvmArgs; + } + + public void addJvmArg(String jvmArg) { + jvmArgs.add(jvmArg); + } + + public String getPluginName() { + return "protoc-gen-" + id; + } + + public File getProtocPlugin() { + return protocPlugin; + } + + public void setProtocPlugin(File protocPlugin) { + this.protocPlugin = protocPlugin; + } + + @Override + public String toString() { + return "DubboProtocPlugin{" + + "id='" + id + '\'' + + ", mainClass='" + mainClass + '\'' + + ", dubboVersion='" + dubboVersion + '\'' + + ", javaHome='" + javaHome + '\'' + + ", pluginDirectory=" + pluginDirectory + + ", resolvedJars=" + resolvedJars + + ", args=" + args + + ", jvmArgs=" + jvmArgs + + ", protocPlugin=" + protocPlugin + + '}'; + } +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapper.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapper.java new file mode 100644 index 00000000000..0124f9e4fa4 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapper.java @@ -0,0 +1,26 @@ +/* + * 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.dubbo.maven.plugin.protoc; + +import org.apache.maven.plugin.logging.Log; + +import java.io.File; + +public interface DubboProtocPluginWrapper { + + File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log); +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapperFactory.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapperFactory.java new file mode 100644 index 00000000000..3a60034b634 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/DubboProtocPluginWrapperFactory.java @@ -0,0 +1,46 @@ +/* + * 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.dubbo.maven.plugin.protoc; + + +import org.codehaus.plexus.util.Os; + +import java.util.HashMap; +import java.util.Map; + +public class DubboProtocPluginWrapperFactory { + + private final LinuxDubboProtocPluginWrapper linuxProtocCommandBuilder = new LinuxDubboProtocPluginWrapper(); + private final WinDubboProtocPluginWrapper winDubboProtocPluginWrapper = new WinDubboProtocPluginWrapper(); + + + private final Map dubboProtocPluginWrappers = new HashMap<>(); + + + public DubboProtocPluginWrapperFactory() { + dubboProtocPluginWrappers.put("linux", linuxProtocCommandBuilder); + dubboProtocPluginWrappers.put("windows", winDubboProtocPluginWrapper); + } + + + public DubboProtocPluginWrapper findByOs() { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + return dubboProtocPluginWrappers.get("windows"); + } + return dubboProtocPluginWrappers.get("linux"); + } +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/LinuxDubboProtocPluginWrapper.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/LinuxDubboProtocPluginWrapper.java new file mode 100644 index 00000000000..639300eb332 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/LinuxDubboProtocPluginWrapper.java @@ -0,0 +1,84 @@ +/* + * 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.dubbo.maven.plugin.protoc; + +import org.apache.maven.plugin.logging.Log; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +public class LinuxDubboProtocPluginWrapper implements DubboProtocPluginWrapper { + + + @Override + public File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log) { + List resolvedJars = dubboProtocPlugin.getResolvedJars(); + createPluginDirectory(dubboProtocPlugin.getPluginDirectory()); + File pluginExecutableFile = new File(dubboProtocPlugin.getPluginDirectory(), dubboProtocPlugin.getPluginName()); + final File javaLocation = new File(dubboProtocPlugin.getJavaHome(), "bin/java"); + + if (log.isDebugEnabled()) { + log.debug("javaLocation=" + javaLocation.getAbsolutePath()); + } + try (final PrintWriter out = new PrintWriter(new FileWriter(pluginExecutableFile))) { + out.println("#!/bin/sh"); + out.println(); + out.print("CP="); + for (int i = 0; i < resolvedJars.size(); i++) { + if (i > 0) { + out.print(":"); + } + out.print("\"" + resolvedJars.get(i).getAbsolutePath() + "\""); + } + out.println(); + out.print("ARGS=\""); + for (final String arg : dubboProtocPlugin.getArgs()) { + out.print(arg + " "); + } + out.println("\""); + out.print("JVMARGS=\""); + for (final String jvmArg : dubboProtocPlugin.getJvmArgs()) { + out.print(jvmArg + " "); + } + out.println("\""); + out.println(); + out.println("\"" + javaLocation.getAbsolutePath() + "\" $JVMARGS -cp $CP " + + dubboProtocPlugin.getMainClass() + " $ARGS"); + out.println(); + boolean b = pluginExecutableFile.setExecutable(true); + if (!b) { + throw new RuntimeException("Could not make plugin executable: " + pluginExecutableFile); + } + return pluginExecutableFile; + } catch (IOException e) { + throw new RuntimeException("Could not write plugin script file: " + pluginExecutableFile, e); + } + + } + + + private void createPluginDirectory(File pluginDirectory) { + pluginDirectory.mkdirs(); + if (!pluginDirectory.isDirectory()) { + throw new RuntimeException("Could not create protoc plugin directory: " + + pluginDirectory.getAbsolutePath()); + } + } +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/ProtocMetaData.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/ProtocMetaData.java new file mode 100644 index 00000000000..277d53b4810 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/ProtocMetaData.java @@ -0,0 +1,85 @@ +/* + * 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.dubbo.maven.plugin.protoc; + + +import java.io.File; +import java.util.List; + +public class ProtocMetaData { + + private String protocExecutable; + private File protoSourceDir; + private List protoFiles; + private File outputDir; + private DubboProtocPlugin dubboProtocPlugin; + + public ProtocMetaData() { + + } + + public ProtocMetaData(String protocExecutable, File protoSourceDir, List protoFiles, File outputDir, DubboProtocPlugin dubboProtocPlugin) { + this.protocExecutable = protocExecutable; + this.protoSourceDir = protoSourceDir; + this.protoFiles = protoFiles; + this.outputDir = outputDir; + this.dubboProtocPlugin = dubboProtocPlugin; + } + + + public String getProtocExecutable() { + return protocExecutable; + } + + public void setProtocExecutable(String protocExecutable) { + this.protocExecutable = protocExecutable; + } + + public File getProtoSourceDir() { + return protoSourceDir; + } + + public void setProtoSourceDir(File protoSourceDir) { + this.protoSourceDir = protoSourceDir; + } + + public List getProtoFiles() { + return protoFiles; + } + + public void setProtoFiles(List protoFiles) { + this.protoFiles = protoFiles; + } + + public File getOutputDir() { + return outputDir; + } + + public void setOutputDir(File outputDir) { + this.outputDir = outputDir; + } + + public DubboProtocPlugin getDubboProtocPlugin() { + return dubboProtocPlugin; + } + + public void setDubboProtocPlugin(DubboProtocPlugin dubboProtocPlugin) { + this.dubboProtocPlugin = dubboProtocPlugin; + } + + +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/WinDubboProtocPluginWrapper.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/WinDubboProtocPluginWrapper.java new file mode 100644 index 00000000000..f28a8c5c9f5 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/WinDubboProtocPluginWrapper.java @@ -0,0 +1,153 @@ +/* + * 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.dubbo.maven.plugin.protoc; + +import org.apache.maven.plugin.logging.Log; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URL; + +public class WinDubboProtocPluginWrapper implements DubboProtocPluginWrapper { + + private static final String DATA_MODEL_SYSPROP = "sun.arch.data.model"; + + private static final String WIN_JVM_DATA_MODEL_32 = "32"; + + private static final String WIN_JVM_DATA_MODEL_64 = "64"; + + private String winJvmDataModel; + + private static File findJvmLocation(final File javaHome, final String... paths) { + for (final String path : paths) { + final File jvmLocation = new File(javaHome, path); + if (jvmLocation.isFile()) { + return jvmLocation; + } + } + return null; + } + + @Override + public File createProtocPlugin(DubboProtocPlugin dubboProtocPlugin, Log log) { + createPluginDirectory(dubboProtocPlugin.getPluginDirectory()); + final File javaHome = new File(dubboProtocPlugin.getJavaHome()); + final File jvmLocation = findJvmLocation(javaHome, + "jre/bin/server/jvm.dll", + "bin/server/jvm.dll", + "jre/bin/client/jvm.dll", + "bin/client/jvm.dll"); + final File winRun4JIniFile = new File(dubboProtocPlugin.getPluginDirectory(), dubboProtocPlugin.getId() + ".ini"); + + if (winJvmDataModel != null) { + if (!(winJvmDataModel.equals(WIN_JVM_DATA_MODEL_32) || winJvmDataModel.equals(WIN_JVM_DATA_MODEL_64))) { + throw new RuntimeException("winJvmDataModel must be '32' or '64'"); + } + } else if (archDirectoryExists("amd64", dubboProtocPlugin.getJavaHome())) { + winJvmDataModel = WIN_JVM_DATA_MODEL_64; + if (log.isDebugEnabled()) { + log.debug("detected 64-bit JVM from directory structure"); + } + } else if (archDirectoryExists("i386", dubboProtocPlugin.getJavaHome())) { + winJvmDataModel = WIN_JVM_DATA_MODEL_32; + if (log.isDebugEnabled()) { + log.debug("detected 32-bit JVM from directory structure"); + } + } else if (System.getProperty(DATA_MODEL_SYSPROP) != null) { + winJvmDataModel = System.getProperty(DATA_MODEL_SYSPROP); + if (log.isDebugEnabled()) { + log.debug("detected " + winJvmDataModel + "-bit JVM from system property " + DATA_MODEL_SYSPROP); + } + } else { + winJvmDataModel = WIN_JVM_DATA_MODEL_32; + if (log.isDebugEnabled()) { + log.debug("defaulting to 32-bit JVM"); + } + } + try (final PrintWriter out = new PrintWriter(new FileWriter(winRun4JIniFile))) { + if (jvmLocation != null) { + out.println("vm.location=" + jvmLocation.getAbsolutePath()); + } + int index = 1; + for (final File resolvedJar : dubboProtocPlugin.getResolvedJars()) { + out.println("classpath." + index + "=" + resolvedJar.getAbsolutePath()); + index++; + } + out.println("main.class=" + dubboProtocPlugin.getMainClass()); + + index = 1; + for (final String arg : dubboProtocPlugin.getArgs()) { + out.println("arg." + index + "=" + arg); + index++; + } + + index = 1; + for (final String jvmArg : dubboProtocPlugin.getJvmArgs()) { + out.println("vmarg." + index + "=" + jvmArg); + index++; + } + + out.println("vm.version.min=1.8"); + out.println("log.level=none"); + out.println("[ErrorMessages]"); + out.println("show.popup=false"); + } catch (IOException e) { + throw new RuntimeException( + "Could not write WinRun4J ini file: " + winRun4JIniFile.getAbsolutePath(), e); + } + final String executablePath = getWinrun4jExecutablePath(); + final URL url = Thread.currentThread().getContextClassLoader().getResource(executablePath); + if (url == null) { + throw new RuntimeException( + "Could not locate WinRun4J executable at path: " + executablePath); + } + File pluginExecutableFile = getPluginExecutableFile(dubboProtocPlugin); + try { + FileUtils.copyURLToFile(url, pluginExecutableFile); + return pluginExecutableFile; + } catch (IOException e) { + throw new RuntimeException( + "Could not copy WinRun4J executable to: " + pluginExecutableFile.getAbsolutePath(), e); + } + } + + private void createPluginDirectory(File pluginDirectory) { + pluginDirectory.mkdirs(); + if (!pluginDirectory.isDirectory()) { + throw new RuntimeException("Could not create protoc plugin directory: " + + pluginDirectory.getAbsolutePath()); + } + } + + private boolean archDirectoryExists(String arch, String javaHome) { + return javaHome != null + && (new File(javaHome, "jre/lib/" + arch).isDirectory() + || new File(javaHome, "lib/" + arch).isDirectory()); + } + + private String getWinrun4jExecutablePath() { + return "winrun4j/WinRun4J" + winJvmDataModel + ".exe"; + } + + public File getPluginExecutableFile(DubboProtocPlugin dubboProtocPlugin) { + return new File(dubboProtocPlugin.getPluginDirectory(), "protoc-gen-" + dubboProtocPlugin.getId() + ".exe"); + } + +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/DefaultProtocCommandBuilder.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/DefaultProtocCommandBuilder.java new file mode 100644 index 00000000000..c5ef8b4282f --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/DefaultProtocCommandBuilder.java @@ -0,0 +1,43 @@ +/* + * 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.dubbo.maven.plugin.protoc.command; + +import org.apache.dubbo.maven.plugin.protoc.DubboProtocPlugin; +import org.apache.dubbo.maven.plugin.protoc.ProtocMetaData; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class DefaultProtocCommandBuilder implements ProtocCommandArgsBuilder { + + @Override + public List buildProtocCommandArgs(ProtocMetaData protocMetaData) { + final List command = new ArrayList<>(); + command.add("--proto_path=" + protocMetaData.getProtoSourceDir()); + String outputOption = "--java_out="; + outputOption += protocMetaData.getOutputDir(); + command.add(outputOption); + DubboProtocPlugin dubboProtocPlugin = protocMetaData.getDubboProtocPlugin(); + command.add("--plugin=protoc-gen-" + dubboProtocPlugin.getId() + '=' + dubboProtocPlugin.getProtocPlugin()); + command.add("--" + dubboProtocPlugin.getId() + "_out=" + protocMetaData.getOutputDir()); + for (final File protoFile : protocMetaData.getProtoFiles()) { + command.add(protoFile.toString()); + } + return command; + } +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/ProtocCommandArgsBuilder.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/ProtocCommandArgsBuilder.java new file mode 100644 index 00000000000..f00de413799 --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/command/ProtocCommandArgsBuilder.java @@ -0,0 +1,26 @@ +/* + * 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.dubbo.maven.plugin.protoc.command; + +import org.apache.dubbo.maven.plugin.protoc.ProtocMetaData; + +import java.util.List; + +public interface ProtocCommandArgsBuilder { + + List buildProtocCommandArgs(ProtocMetaData protocMetaData) throws RuntimeException; +} diff --git a/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/enums/DubboGenerateTypeEnum.java b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/enums/DubboGenerateTypeEnum.java new file mode 100644 index 00000000000..2157e6eee1b --- /dev/null +++ b/dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/protoc/enums/DubboGenerateTypeEnum.java @@ -0,0 +1,60 @@ +/* + * 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.dubbo.maven.plugin.protoc.enums; + +public enum DubboGenerateTypeEnum { + Dubbo("dubbo","org.apache.dubbo.gen.dubbo.DubboGenerator"), + Dubbo3("dubbo3","org.apache.dubbo.gen.dubbo.Dubbo3Generator"), + Tri("tri","org.apache.dubbo.gen.tri.Dubbo3TripleGenerator"), + Tri_reactor("tri_reactor","org.apache.dubbo.gen.tri.reactive.ReactorDubbo3TripleGenerator"), + Grpc("grpc","org.apache.dubbo.gen.grpc.DubboGrpcGenerator"), + Grpc_reactor("grpc_reactor","org.apache.dubbo.gen.grpc.reactive.ReactorDubboGrpcGenerator"), + Grpc_rx("grpc_rx","org.apache.dubbo.gen.grpc.reactive.RxDubboGrpcGenerator"), + ; + private String id; + private String mainClass; + DubboGenerateTypeEnum(String id, String mainClass) { + this.id = id; + this.mainClass = mainClass; + } + + public static DubboGenerateTypeEnum getByType(String dubboGenerateType) { + DubboGenerateTypeEnum[] values = DubboGenerateTypeEnum.values(); + for (DubboGenerateTypeEnum value : values) { + if (value.getId().equals(dubboGenerateType)) { + return value; + } + } + return null; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getMainClass() { + return mainClass; + } + + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + } +} diff --git a/pom.xml b/pom.xml index 63b42414c34..846ad34ec61 100644 --- a/pom.xml +++ b/pom.xml @@ -367,6 +367,8 @@ **/org/apache/dubbo/metrics/aggregate/DubboAbstractTDigest.java, **/org/apache/dubbo/common/logger/helpers/FormattingTuple.java, **/org/apache/dubbo/common/logger/helpers/MessageFormatter.java, + **/org/apache/dubbo/maven/plugin/protoc/DubboProtocCompilerMojo.java, + **/org/apache/dubbo/gen/utils/ProtoTypeMap.java, **/istio/v1/auth/**/*, **/com/google/rpc/*, **/generated/**/*,