diff --git a/client/native/rocketMQ.h b/client/native/rocketMQ.h new file mode 100644 index 0000000000000000000000000000000000000000..51b363f940468a89e137591a2137dc0a6f727ebf --- /dev/null +++ b/client/native/rocketMQ.h @@ -0,0 +1,15 @@ +#ifndef ROCKETMQ_H_ +#define ROCKETMQ_H_ + + +typedef struct Message_Send{ + char* producer_name; + char* topic; + char* tags; + char* keys; + char* body; +}Message_Send_Struct; + + + +#endif /* ROCKETMQ_H_ */ \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml index afa47c07e8498178bea081a2bfb5e0244eed312a..72c0454588ab4d4b5800a41e0ff51f617974613c 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -33,6 +33,12 @@ + + + com.oracle.substratevm + svm + 1.0.0-rc16-dev + ${project.groupId} rocketmq-common diff --git a/client/src/main/java/org/apache/rocketmq/client/Producer.java b/client/src/main/java/org/apache/rocketmq/client/Producer.java index ff87cd6d498c3a28f827fb0ed03819c7c3d034b6..5f7d0b8083afbb7ae7ce089b9c2419c1cb07d1a7 100644 --- a/client/src/main/java/org/apache/rocketmq/client/Producer.java +++ b/client/src/main/java/org/apache/rocketmq/client/Producer.java @@ -3,39 +3,164 @@ Main entry for building native image */ package org.apache.rocketmq.client; +import java.util.Collections; +import java.util.List; + +import org.apache.rocketmq.client.Producer.CRocketMQDirectives; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; -import com.alibaba.fastjson.parser.ParserConfig; +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.struct.CField; +import org.graalvm.nativeimage.c.struct.CStruct; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.word.PointerBase; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.logging.LogLevel; +import com.alibaba.fastjson.parser.ParserConfig; +import com.oracle.svm.core.c.ProjectHeaderFile; +/** + * @CContext annotation tells this class providing the context to interact with + * C context. This is required to build as a SO library file, but not necessary to + * build an executable file. + * + * @author cengfeng.lzy + * + */ +@CContext(CRocketMQDirectives.class) public class Producer { - private static final LoggingHandler log = new LoggingHandler(LogLevel.DEBUG); - public static void main(String[] args) throws MQClientException, InterruptedException { - ParserConfig.global.setAsmEnable(false); - DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); - producer.start(); - - for (int i = 0; i < 128; i++) - try { - { - Message msg = new Message("TopicTest", - "TagA", - "OrderID188", - "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); - SendResult sendResult = producer.send(msg); - System.out.printf("%s%n", sendResult); - } - - } catch (Exception e) { - e.printStackTrace(); - } - - producer.shutdown(); - } + static class CRocketMQDirectives implements CContext.Directives { + + @Override + public List getHeaderFiles() { + /* + * The header file with the C declarations that are imported. Here we give the + * name of the header file. SVM searches the header file according to the file + * name specified here and the relative path specified in H:CLibraryPath in + * option. + */ + return Collections.singletonList(ProjectHeaderFile.resolve("client", "rocketMQ.h")); + } + } + + /** + * This interface gives a Java version description of Message_Send_Struct data + * structure defined in the C header file. + * + * This declaration MUST be enclosed inside the @CContext class. + * + * @author cengfeng.lzy + * + */ + @CStruct("Message_Send_Struct") + interface CMessageSendStruct extends PointerBase { + @CField("producer_name") + CCharPointer getProducerName(); + + @CField("producer_name") + void setProducerName(CCharPointer value); + + @CField("topic") + CCharPointer getTopic(); + + @CField("topic") + void setTopic(CCharPointer value); + + @CField("tags") + CCharPointer getTags(); + + @CField("tags") + void setTags(CCharPointer value); + + @CField("keys") + CCharPointer getKeys(); + + @CField("keys") + void setKeys(CCharPointer value); + + @CField("body") + CCharPointer getBody(); + + @CField("body") + void setBody(CCharPointer value); + } + + /** + * This main method is used to generate an executable file by SVM. + * @param args + * @throws MQClientException + * @throws InterruptedException + */ + public static void main(String[] args) throws MQClientException, InterruptedException { + ParserConfig.global.setAsmEnable(false); + DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); + producer.start(); + + for (int i = 0; i < 128; i++) + try { + { + Message msg = new Message("TopicTest", "TagA", "OrderID188", + "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); + SendResult sendResult = producer.send(msg); + System.out.printf("%s%n", sendResult); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + producer.shutdown(); + } + + /** + * This example shows how to expose an API with complex data structure + * parameter. This API wraps SendResult + * org.apache.rocketmq.client.producer.DefaultMQProducer.send(Message msg) + * + * @param thread isolated thread is required by SVM + * @param cmessageSends correspond to the Message_Send_Struct defined in the C + * header file. + * @return CCharPointer corresponds to char * in C + */ + @CEntryPoint(name = "send_message") + public static CCharPointer send(IsolateThread thread, CMessageSendStruct cmessageSends) { + // Disable dynamic class generation and class loading in Fastjson + ParserConfig.global.setAsmEnable(false); + DefaultMQProducer producer = new DefaultMQProducer( + // Here shows how to get a char * to String + CTypeConversion.toJavaString(cmessageSends.getProducerName())); + try { + producer.start(); + } catch (MQClientException e1) { + e1.printStackTrace(); + // Here shows how to convert null to char *. + // As the returned value must be of WordBase type, but null is of Object type. + // So we cannot return a null directly, but have to convert it to a + // CCharPointer. + return CTypeConversion.toCString(null).get(); + } + + String topic = CTypeConversion.toJavaString(cmessageSends.getTopic()); // TopicTest + String tags = CTypeConversion.toJavaString(cmessageSends.getTags()); // TagA + String key = CTypeConversion.toJavaString(cmessageSends.getKeys()); // OrderID188 + String body = CTypeConversion.toJavaString(cmessageSends.getBody()); // Hello world + try { + // Construct a Message instance from the data extracted from C structure. + Message msg = new Message(topic, tags, key, body.getBytes(RemotingHelper.DEFAULT_CHARSET)); + SendResult sendResult = producer.send(msg); + // Return string contents in SendResult instance. + return CTypeConversion.toCString(sendResult.toString()).get(); + } catch (Exception e) { + e.printStackTrace(); + return CTypeConversion.toCString(null).get(); + } finally { + producer.shutdown(); + } + } } diff --git a/client/svm.sh b/client/svm.sh index e13cfcff2d9e7c2316d7cd7f5aa12d8feab5f071..b56062d5db8eb43c1bdef479603ac0d4016cffc4 100755 --- a/client/svm.sh +++ b/client/svm.sh @@ -7,9 +7,7 @@ SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=io.netty.handler.ssl SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=io.netty.handler.ssl.JdkAlpnSslEngine" SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=io.netty.util.internal.JavassistTypeParameterMatcherGenerator" SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=com.alibaba.fastjson.serializer.JodaCodec" -# testing -#SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=io.netty.handler.ssl.util.SelfSignedCertificate" -#SVM_OPT="${SVM_OPT} --delay-class-initialization-to-runtime=io.netty.handler.ssl.util.ThreadLocalInsecureRandom" + SVM_OPT="${SVM_OPT} --rerun-class-initialization-at-runtime=io.netty.handler.ssl.util.SelfSignedCertificate" SVM_OPT="${SVM_OPT} --rerun-class-initialization-at-runtime=io.netty.handler.ssl.util.ThreadLocalInsecureRandom" SVM_OPT="${SVM_OPT} --rerun-class-initialization-at-runtime=com.alibaba.fastjson.serializer.SerializeConfig" @@ -18,7 +16,16 @@ SVM_OPT="${SVM_OPT} --rerun-class-initialization-at-runtime=com.alibaba.fastjson SVM_OPT="${SVM_OPT} --enable-url-protocols=http" WORKDIR=`pwd` -CONFIG_OPT="-H:ConfigurationFileDirectories=${WORKDIR}/config -Dio.netty.noUnsafe=true -H:+ReportExceptionStackTraces --allow-incomplete-classpath" -native_image=/home/cengfeng.lzy/GraalDev/graal/vm/mxbuild/linux-amd64/GRAALVM_LIBGRAAL/graalvm-libgraal-1.0.0-rc16-dev/bin/native-image -#native_image=~/tools/graalvm-ce-1.0.0-rc14/bin/native-image +CONFIG_OPT="-H:ConfigurationFileDirectories=${WORKDIR}/config -H:+ReportExceptionStackTraces --allow-incomplete-classpath" + +#Disable unsafe usage in netty. This option is provided by netty, not an univeral solution. A more general way +#is to use Graal's substition mechenism (see "Unsafe memory access" in +#https://medium.com/graalvm/instant-netty-startup-using-graalvm-native-image-generation-ed6f14ff7692) +CONFIG_OPT="${CONFIG_OPT} -Dio.netty.noUnsafe=true" +#Compile to a SO file +CONFIG_OPT="${CONFIG_OPT} --shared -H:Name=rocketMQClient" +#Specify where is the C library file which defines the data structure used in exposed API. +CONFIG_OPT="${CONFIG_OPT} -H:CLibraryPath=native" + +#Set your own $native_image enviroment variable which should refer to the bin\native-image file in your graalvm JDK. $native_image $CONFIG_OPT $SVM_OPT -jar target/rocketmq-client-4.5.1-SNAPSHOT-jar-with-dependencies.jar