Commit 024b89fb authored by rawan's avatar rawan

Initial commit: Completed gRPC project (Python, Go, Java)

parents
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
# Python cache
__pycache__/
*.pyc
# Go executables
*.exe
# Our Project's Log files
*.log
*.txt
!README.md
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="17602b36-122a-44f0-b856-10497abebbb1" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/addition_service/add_server.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/multiplication_service/server.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/proto/calculator.proto" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/com/example/grpc/calculator/ClientGateway.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/example/Main.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 1
}</component>
<component name="ProjectId" id="35NvdHz0umpu6faPy2pmD4go5cf" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Application.ClientGateway.executor": "Run",
"Maven.gRPC_java [compile].executor": "Run",
"Maven.grpc-calculator [compile].executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"git-widget-placeholder": "master",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "D:/fifth year/DS/grpc-calculator/pom.xml"
}
}]]></component>
<component name="RunManager">
<configuration name="ClientGateway" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.example.grpc.calculator.ClientGateway" />
<module name="grpc-calculator" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.example.grpc.calculator.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="Application.ClientGateway" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="17602b36-122a-44f0-b856-10497abebbb1" name="Changes" comment="" />
<created>1762963431458</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1762963431458</updated>
</task>
<servers />
</component>
<component name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.proto" />
</component>
</project>
\ No newline at end of file
import grpc
from concurrent import futures
import time
# Import the files we generated
import calculator_pb2
import calculator_pb2_grpc
# Specify the name of the log file
LOG_FILE = "addition_log.txt"
#Inherit must inherit from the class generated in the _grpc.py file
class AdditionService(calculator_pb2_grpc.AdditionServiceServicer):
#2. Apply the Add function
def Add(self, request, context):
print(f"Received Add request: num1={request.num1}, num2={request.num2}")
result = request.num1 + request.num2
log_message = f"Add Operation: {request.num1} + {request.num2} = {result}\n"
# --- Recording the operation in the log ---
try:
with open(LOG_FILE, "a") as f:
f.write(log_message)
except Exception as e:
print(f"Failed to write to log: {e}")
# Return the response (must be of type OperationResponse)
return calculator_pb2.OperationResponse(result=result)
#3. Implement the log broadcast function (GetAdditionLog)
def GetAdditionLog(self, request, context):
print("Received GetLog request. Streaming log...")
try:
#Open the log file for reading
with open(LOG_FILE, "r") as f:
# Read line by line
for line in f:
#Send each line as a "stream" to the client
yield calculator_pb2.LogEntry(message=line.strip())
time.sleep(0.1)
except FileNotFoundError:
print("Log file not found.")
# Sending one error message
yield calculator_pb2.LogEntry(message="Log file not found.")
except Exception as e:
print(f"Error reading log file: {e}")
yield calculator_pb2.LogEntry(message=f"Error reading log file: {e}")
#4. The main function to operate the server
def serve():
# Create a server with 10 threads
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
#Add our service to the server
calculator_pb2_grpc.add_AdditionServiceServicer_to_server(AdditionService(), server)
#Select the port
port = "50051"
server.add_insecure_port(f"[::]:{port}")
server.start()
print(f"Addition Service server started on port {port}...")
#Keep the server running
try:
while True:
time.sleep(86400) #1 day (to keep the main thread alive)
except KeyboardInterrupt:
print("Stopping server...")
server.stop(0)
if __name__ == "__main__":
serve()
\ No newline at end of file
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: calculator.proto
# Protobuf Python Version: 6.31.1
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
31,
1,
'',
'calculator.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x63\x61lculator.proto\x12\ncalculator\".\n\x10OperationRequest\x12\x0c\n\x04num1\x18\x01 \x01(\x05\x12\x0c\n\x04num2\x18\x02 \x01(\x05\"#\n\x11OperationResponse\x12\x0e\n\x06result\x18\x01 \x01(\x05\"\x0f\n\rGetLogRequest\"\x1b\n\x08LogEntry\x12\x0f\n\x07message\x18\x01 \x01(\t2\x9a\x01\n\x0f\x41\x64\x64itionService\x12\x42\n\x03\x41\x64\x64\x12\x1c.calculator.OperationRequest\x1a\x1d.calculator.OperationResponse\x12\x43\n\x0eGetAdditionLog\x12\x19.calculator.GetLogRequest\x1a\x14.calculator.LogEntry0\x01\x32\xab\x01\n\x15MultiplicationService\x12G\n\x08Multiply\x12\x1c.calculator.OperationRequest\x1a\x1d.calculator.OperationResponse\x12I\n\x14GetMultiplicationLog\x12\x19.calculator.GetLogRequest\x1a\x14.calculator.LogEntry0\x01\x42@\n\x1b\x63om.example.grpc.calculatorB\x0f\x43\x61lculatorProtoP\x01Z\x0e./calculatorpbb\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'calculator_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
_globals['DESCRIPTOR']._loaded_options = None
_globals['DESCRIPTOR']._serialized_options = b'\n\033com.example.grpc.calculatorB\017CalculatorProtoP\001Z\016./calculatorpb'
_globals['_OPERATIONREQUEST']._serialized_start=32
_globals['_OPERATIONREQUEST']._serialized_end=78
_globals['_OPERATIONRESPONSE']._serialized_start=80
_globals['_OPERATIONRESPONSE']._serialized_end=115
_globals['_GETLOGREQUEST']._serialized_start=117
_globals['_GETLOGREQUEST']._serialized_end=132
_globals['_LOGENTRY']._serialized_start=134
_globals['_LOGENTRY']._serialized_end=161
_globals['_ADDITIONSERVICE']._serialized_start=164
_globals['_ADDITIONSERVICE']._serialized_end=318
_globals['_MULTIPLICATIONSERVICE']._serialized_start=321
_globals['_MULTIPLICATIONSERVICE']._serialized_end=492
# @@protoc_insertion_point(module_scope)
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
import calculator_pb2 as calculator__pb2
GRPC_GENERATED_VERSION = '1.76.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
raise RuntimeError(
f'The grpc package installed is at version {GRPC_VERSION},'
+ ' but the generated code in calculator_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
)
class AdditionServiceStub(object):
"""------------------ الخدمات (الوظائف) ------------------
الخدمة الثانية: خدمة الجمع
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Add = channel.unary_unary(
'/calculator.AdditionService/Add',
request_serializer=calculator__pb2.OperationRequest.SerializeToString,
response_deserializer=calculator__pb2.OperationResponse.FromString,
_registered_method=True)
self.GetAdditionLog = channel.unary_stream(
'/calculator.AdditionService/GetAdditionLog',
request_serializer=calculator__pb2.GetLogRequest.SerializeToString,
response_deserializer=calculator__pb2.LogEntry.FromString,
_registered_method=True)
class AdditionServiceServicer(object):
"""------------------ الخدمات (الوظائف) ------------------
الخدمة الثانية: خدمة الجمع
"""
def Add(self, request, context):
"""دالة الجمع: تستقبل رقمين وترجع الناتج
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def GetAdditionLog(self, request, context):
"""دالة سجل العمليات: تستقبل طلباً فارغاً وترجع بثاً (stream) من السجلات
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_AdditionServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'Add': grpc.unary_unary_rpc_method_handler(
servicer.Add,
request_deserializer=calculator__pb2.OperationRequest.FromString,
response_serializer=calculator__pb2.OperationResponse.SerializeToString,
),
'GetAdditionLog': grpc.unary_stream_rpc_method_handler(
servicer.GetAdditionLog,
request_deserializer=calculator__pb2.GetLogRequest.FromString,
response_serializer=calculator__pb2.LogEntry.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'calculator.AdditionService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('calculator.AdditionService', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class AdditionService(object):
"""------------------ الخدمات (الوظائف) ------------------
الخدمة الثانية: خدمة الجمع
"""
@staticmethod
def Add(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/calculator.AdditionService/Add',
calculator__pb2.OperationRequest.SerializeToString,
calculator__pb2.OperationResponse.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def GetAdditionLog(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(
request,
target,
'/calculator.AdditionService/GetAdditionLog',
calculator__pb2.GetLogRequest.SerializeToString,
calculator__pb2.LogEntry.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
class MultiplicationServiceStub(object):
"""الخدمة الثالثة: خدمة الضرب
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Multiply = channel.unary_unary(
'/calculator.MultiplicationService/Multiply',
request_serializer=calculator__pb2.OperationRequest.SerializeToString,
response_deserializer=calculator__pb2.OperationResponse.FromString,
_registered_method=True)
self.GetMultiplicationLog = channel.unary_stream(
'/calculator.MultiplicationService/GetMultiplicationLog',
request_serializer=calculator__pb2.GetLogRequest.SerializeToString,
response_deserializer=calculator__pb2.LogEntry.FromString,
_registered_method=True)
class MultiplicationServiceServicer(object):
"""الخدمة الثالثة: خدمة الضرب
"""
def Multiply(self, request, context):
"""دالة الضرب: تستقبل رقمين وترجع الناتج
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def GetMultiplicationLog(self, request, context):
"""دالة سجل العمليات: تستقبل طلباً فارغاً وترجع بثاً (stream) من السجلات
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_MultiplicationServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'Multiply': grpc.unary_unary_rpc_method_handler(
servicer.Multiply,
request_deserializer=calculator__pb2.OperationRequest.FromString,
response_serializer=calculator__pb2.OperationResponse.SerializeToString,
),
'GetMultiplicationLog': grpc.unary_stream_rpc_method_handler(
servicer.GetMultiplicationLog,
request_deserializer=calculator__pb2.GetLogRequest.FromString,
response_serializer=calculator__pb2.LogEntry.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'calculator.MultiplicationService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('calculator.MultiplicationService', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class MultiplicationService(object):
"""الخدمة الثالثة: خدمة الضرب
"""
@staticmethod
def Multiply(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/calculator.MultiplicationService/Multiply',
calculator__pb2.OperationRequest.SerializeToString,
calculator__pb2.OperationResponse.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def GetMultiplicationLog(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(
request,
target,
'/calculator.MultiplicationService/GetMultiplicationLog',
calculator__pb2.GetLogRequest.SerializeToString,
calculator__pb2.LogEntry.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
// تحديد إصدار الـ syntax المستخدم
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc v6.33.0
// source: calculator.proto
// (اختياري) تحديد package لتجنب تضارب الأسماء
// مفيد جداً خاصة لـ Java و Go
package calculatorpb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// رسالة الطلب التي تحتوي على الرقمين
type OperationRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Num1 int32 `protobuf:"varint,1,opt,name=num1,proto3" json:"num1,omitempty"`
Num2 int32 `protobuf:"varint,2,opt,name=num2,proto3" json:"num2,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OperationRequest) Reset() {
*x = OperationRequest{}
mi := &file_calculator_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *OperationRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OperationRequest) ProtoMessage() {}
func (x *OperationRequest) ProtoReflect() protoreflect.Message {
mi := &file_calculator_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OperationRequest.ProtoReflect.Descriptor instead.
func (*OperationRequest) Descriptor() ([]byte, []int) {
return file_calculator_proto_rawDescGZIP(), []int{0}
}
func (x *OperationRequest) GetNum1() int32 {
if x != nil {
return x.Num1
}
return 0
}
func (x *OperationRequest) GetNum2() int32 {
if x != nil {
return x.Num2
}
return 0
}
// رسالة الرد التي تحتوي على الناتج
type OperationResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Result int32 `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OperationResponse) Reset() {
*x = OperationResponse{}
mi := &file_calculator_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *OperationResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OperationResponse) ProtoMessage() {}
func (x *OperationResponse) ProtoReflect() protoreflect.Message {
mi := &file_calculator_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OperationResponse.ProtoReflect.Descriptor instead.
func (*OperationResponse) Descriptor() ([]byte, []int) {
return file_calculator_proto_rawDescGZIP(), []int{1}
}
func (x *OperationResponse) GetResult() int32 {
if x != nil {
return x.Result
}
return 0
}
// رسالة فارغة تُستخدم لطلب سجل العمليات
type GetLogRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetLogRequest) Reset() {
*x = GetLogRequest{}
mi := &file_calculator_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetLogRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetLogRequest) ProtoMessage() {}
func (x *GetLogRequest) ProtoReflect() protoreflect.Message {
mi := &file_calculator_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetLogRequest.ProtoReflect.Descriptor instead.
func (*GetLogRequest) Descriptor() ([]byte, []int) {
return file_calculator_proto_rawDescGZIP(), []int{2}
}
// رسالة تمثل سطراً واحداً في سجل العمليات
type LogEntry struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogEntry) Reset() {
*x = LogEntry{}
mi := &file_calculator_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogEntry) ProtoMessage() {}
func (x *LogEntry) ProtoReflect() protoreflect.Message {
mi := &file_calculator_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead.
func (*LogEntry) Descriptor() ([]byte, []int) {
return file_calculator_proto_rawDescGZIP(), []int{3}
}
func (x *LogEntry) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_calculator_proto protoreflect.FileDescriptor
const file_calculator_proto_rawDesc = "" +
"\n" +
"\x10calculator.proto\x12\n" +
"calculator\":\n" +
"\x10OperationRequest\x12\x12\n" +
"\x04num1\x18\x01 \x01(\x05R\x04num1\x12\x12\n" +
"\x04num2\x18\x02 \x01(\x05R\x04num2\"+\n" +
"\x11OperationResponse\x12\x16\n" +
"\x06result\x18\x01 \x01(\x05R\x06result\"\x0f\n" +
"\rGetLogRequest\"$\n" +
"\bLogEntry\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage2\x9a\x01\n" +
"\x0fAdditionService\x12B\n" +
"\x03Add\x12\x1c.calculator.OperationRequest\x1a\x1d.calculator.OperationResponse\x12C\n" +
"\x0eGetAdditionLog\x12\x19.calculator.GetLogRequest\x1a\x14.calculator.LogEntry0\x012\xab\x01\n" +
"\x15MultiplicationService\x12G\n" +
"\bMultiply\x12\x1c.calculator.OperationRequest\x1a\x1d.calculator.OperationResponse\x12I\n" +
"\x14GetMultiplicationLog\x12\x19.calculator.GetLogRequest\x1a\x14.calculator.LogEntry0\x01Ba\n" +
"\x1bcom.example.grpc.calculatorB\x0fCalculatorProtoP\x01Z/example.com/multiplication_service/calculatorpbb\x06proto3"
var (
file_calculator_proto_rawDescOnce sync.Once
file_calculator_proto_rawDescData []byte
)
func file_calculator_proto_rawDescGZIP() []byte {
file_calculator_proto_rawDescOnce.Do(func() {
file_calculator_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_calculator_proto_rawDesc), len(file_calculator_proto_rawDesc)))
})
return file_calculator_proto_rawDescData
}
var file_calculator_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_calculator_proto_goTypes = []any{
(*OperationRequest)(nil), // 0: calculator.OperationRequest
(*OperationResponse)(nil), // 1: calculator.OperationResponse
(*GetLogRequest)(nil), // 2: calculator.GetLogRequest
(*LogEntry)(nil), // 3: calculator.LogEntry
}
var file_calculator_proto_depIdxs = []int32{
0, // 0: calculator.AdditionService.Add:input_type -> calculator.OperationRequest
2, // 1: calculator.AdditionService.GetAdditionLog:input_type -> calculator.GetLogRequest
0, // 2: calculator.MultiplicationService.Multiply:input_type -> calculator.OperationRequest
2, // 3: calculator.MultiplicationService.GetMultiplicationLog:input_type -> calculator.GetLogRequest
1, // 4: calculator.AdditionService.Add:output_type -> calculator.OperationResponse
3, // 5: calculator.AdditionService.GetAdditionLog:output_type -> calculator.LogEntry
1, // 6: calculator.MultiplicationService.Multiply:output_type -> calculator.OperationResponse
3, // 7: calculator.MultiplicationService.GetMultiplicationLog:output_type -> calculator.LogEntry
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_calculator_proto_init() }
func file_calculator_proto_init() {
if File_calculator_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_calculator_proto_rawDesc), len(file_calculator_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 2,
},
GoTypes: file_calculator_proto_goTypes,
DependencyIndexes: file_calculator_proto_depIdxs,
MessageInfos: file_calculator_proto_msgTypes,
}.Build()
File_calculator_proto = out.File
file_calculator_proto_goTypes = nil
file_calculator_proto_depIdxs = nil
}
This diff is collapsed.
module example.com/multiplication_service
go 1.25.4
require (
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10
)
require (
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
)
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"os"
"time"
//Import the code we generated
"example.com/multiplication_service/calculatorpb"
"google.golang.org/grpc"
)
const logFile = "multiplication_log.txt"
type server struct {
calculatorpb.UnimplementedMultiplicationServiceServer
}
// 2. Apply the multiply function
func (*server) Multiply(ctx context.Context, req *calculatorpb.OperationRequest) (*calculatorpb.OperationResponse, error) {
num1 := req.GetNum1()
num2 := req.GetNum2()
fmt.Printf("Received Multiply request: num1=%d, num2=%d\n", num1, num2)
result := num1 * num2
logMessage := fmt.Sprintf("Multiply Operation: %d * %d = %d\n", num1, num2, result)
//---Recording the operation in the log---
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Printf("Failed to open log file: %v", err)
} else {
if _, err := f.WriteString(logMessage); err != nil {
log.Printf("Failed to write to log file: %v", err)
}
f.Close()
}
// Returns the response
return &calculatorpb.OperationResponse{Result: result}, nil
}
//3. Implement the log broadcast function (GetMultiplicationLog)
func (*server) GetMultiplicationLog(req *calculatorpb.GetLogRequest, stream calculatorpb.MultiplicationService_GetMultiplicationLogServer) error {
fmt.Println("Received GetLog request. Streaming log...")
f, err := os.Open(logFile)
if err != nil {
log.Printf("Log file not found: %v", err)
return fmt.Errorf("log file not found")
}
defer f.Close()
//Use buffer for reading
buf := make([]byte, 1024)
for {
n, err := f.Read(buf)
if err == io.EOF {
break
}
if err != nil {
log.Printf("Error reading log file: %v", err)
return err
}
//Sending data as a “stream” to the client
if err := stream.Send(&calculatorpb.LogEntry{Message: string(buf[:n])}); err != nil {
log.Printf("Error sending stream: %v", err)
return err
}
time.Sleep(100 * time.Millisecond) //Simple delay to simulate broadcast
}
return nil
}
func main() {
// Determine the port
port := "50052"
lis, err := net.Listen("tcp", "0.0.0.0:"+port)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
// Create a new gRPC server
s := grpc.NewServer()
// Register our service on the server
calculatorpb.RegisterMultiplicationServiceServer(s, &server{})
log.Printf("Multiplication Service server started on port %s...", port)
// Run the server
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>calculator</groupId>
<artifactId>gRPC_java</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>23</java.version>
<grpc.version>1.54.0</grpc.version>
<protobuf.version>4.28.2</protobuf.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins>
<!-- Protobuf plugin -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<!--suppress UnresolvedMavenProperty -->
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<!--suppress UnresolvedMavenProperty -->
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>${project.basedir}/proto</protoSourceRoot>
<outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
syntax = "proto3";
// Define package to avoid name collisions
package calculator;
// Special settings for Java
option java_multiple_files = true;
option java_package = "com.example.grpc.calculator";
option java_outer_classname = "CalculatorProto";
// Special settings for Go
option go_package = "example.com/multiplication_service/calculatorpb";
message OperationRequest {
int32 num1 = 1;
int32 num2 = 2;
}
message OperationResponse {
int32 result = 1;
}
// Transaction log request message
message GetLogRequest {}
// A message representing a single line in the transaction log
message LogEntry {
string message = 1;
}
// ------------------Services ------------------
// Second service: collection service
service AdditionService {
// Addition function: receives two numbers and returns the result
rpc Add(OperationRequest) returns (OperationResponse);
// Transaction log function: returns a stream of logs
rpc GetAdditionLog(GetLogRequest) returns (stream LogEntry);
}
// Third service: Multiplication service
service MultiplicationService {
// Multiplication function: receives two numbers and returns the result
rpc Multiply(OperationRequest) returns (OperationResponse);
// Transaction log function: returns a stream of logs
rpc GetMultiplicationLog(GetLogRequest) returns (stream LogEntry);
}
\ No newline at end of file
package com.example.grpc.calculator;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.Iterator;
import java.util.Scanner;
public class ClientGateway {
// Create communication "channels" with services
// Channel for collection service (Python) on port 50051
private final ManagedChannel additionChannel;
private final AdditionServiceGrpc.AdditionServiceBlockingStub additionStub;
// Channel for the Go service on port 50052
private final ManagedChannel multiplicationChannel;
private final MultiplicationServiceGrpc.MultiplicationServiceBlockingStub multiplicationStub;
public ClientGateway(String addr1, int port1, String addr2, int port2) {
//Set up the sum channel
this.additionChannel = ManagedChannelBuilder.forAddress(addr1, port1)
.usePlaintext() // Use an unencrypted connection (for testing)
.build();
this.additionStub = AdditionServiceGrpc.newBlockingStub(additionChannel);
// Set up the multiplication channel
this.multiplicationChannel = ManagedChannelBuilder.forAddress(addr2, port2)
.usePlaintext()
.build();
this.multiplicationStub = MultiplicationServiceGrpc.newBlockingStub(multiplicationChannel);
}
// Function to close channels
public void shutdown() {
additionChannel.shutdown();
multiplicationChannel.shutdown();
}
// --- 1. Function calling addition and multiplication operation ---
public void performOperations(int num1, int num2) {
System.out.println("Calling services with num1=" + num1 + ", num2=" + num2);
// Create the request message
OperationRequest request = OperationRequest.newBuilder()
.setNum1(num1)
.setNum2(num2)
.build();
// call collection service (Python)
try {
OperationResponse addResponse = additionStub.add(request);
System.out.println("Addition Result (from Python): " + addResponse.getResult());
} catch (StatusRuntimeException e) {
System.err.println("RPC failed (Addition): " + e.getStatus());
}
// call multiplication service (Go)
try {
OperationResponse multiplyResponse = multiplicationStub.multiply(request);
System.out.println("Multiplication Result (from Go): " + multiplyResponse.getResult());
} catch (StatusRuntimeException e) {
System.err.println("RPC failed (Multiplication): " + e.getStatus());
}
}
// --- 2. Collection record request function (Streaming) ---
public void getAdditionLog() {
System.out.println("\n--- Fetching Addition Log (Python) ---");
GetLogRequest request = GetLogRequest.newBuilder().build();
try {
Iterator<LogEntry> logs = additionStub.getAdditionLog(request);
while (logs.hasNext()) {
System.out.println(logs.next().getMessage());
}
} catch (StatusRuntimeException e) {
System.err.println("RPC failed (Get Addition Log): " + e.getStatus());
}
}
// --- 3. Record multiplication request function (Streaming) ---
public void getMultiplicationLog() {
System.out.println("\n--- Fetching Multiplication Log (Go) ---");
GetLogRequest request = GetLogRequest.newBuilder().build();
try {
Iterator<LogEntry> logs = multiplicationStub.getMultiplicationLog(request);
while (logs.hasNext()) {
System.out.println(logs.next().getMessage());
}
} catch (StatusRuntimeException e) {
System.err.println("RPC failed (Get Multiplication Log): " + e.getStatus());
}
}
//---Main function of client operation---
public static void main(String[] args) {
// Create the client and specify the service addresses
ClientGateway client = new ClientGateway("localhost", 50051, "localhost", 50052);
// Create a Scanner to read from the user
Scanner scanner = new Scanner(System.in);
try {
while (true) {
System.out.println("\n---------------------------------");
System.out.println("Select an option:");
System.out.println("1: Perform Operations (Add & Multiply)");
System.out.println("2: Get Logs (Streaming)");
System.out.println("0: Exit");
System.out.print("Enter your choice: ");
int choice = scanner.nextInt();
if (choice == 1) {
// --- 1. Receive numbers from user ---
System.out.print("Enter number 1: ");
int num1 = scanner.nextInt();
System.out.print("Enter number 2: ");
int num2 = scanner.nextInt();
// --- 2. Sending numbers to services ---
client.performOperations(num1, num2);
} else if (choice == 2) {
// --- 3. Request records (Streaming) ---
client.getAdditionLog();
client.getMultiplicationLog();
} else if (choice == 0) {
System.out.println("Exiting...");
break;
} else {
System.out.println("Invalid choice. Please try again.");
}
}
} finally {
//Close the connections and Scanner when finished
client.shutdown();
scanner.close();
}
}
}
\ No newline at end of file
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment