Skip to content

Commit 2d09343

Browse files
Merge pull request #87 from OmniSharp/feature/json-rpc-server-builder
Added json rpc server builder
2 parents 9fa60ff + 9851f7a commit 2d09343

14 files changed

+423
-104
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"coverage-gutters.lcovname": "*.info"
2+
"coverage-gutters.lcovname": "*.info",
3+
"coverage-gutters.showGutterCoverage": true
34
}

src/JsonRpc/HandlerCollection.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Diagnostics;
55
using System.Linq;
6+
using System.Reactive.Disposables;
67
using System.Reflection;
78
using MediatR;
89

@@ -57,9 +58,13 @@ private void Remove(IJsonRpcHandler handler)
5758
if (i != null) _handlers.Remove(i);
5859
}
5960

60-
public IDisposable Add(IJsonRpcHandler handler)
61+
public IDisposable Add(params IJsonRpcHandler[] handlers)
6162
{
62-
return Add(GetMethodName(handler.GetType()), handler);
63+
var cd = new CompositeDisposable();
64+
foreach (var handler in handlers){
65+
cd.Add(Add(GetMethodName(handler.GetType()), handler));
66+
}
67+
return cd;
6368
}
6469

6570
public IDisposable Add(string method, IJsonRpcHandler handler)

src/JsonRpc/IJsonRpcServer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace OmniSharp.Extensions.JsonRpc
2+
{
3+
public interface IJsonRpcServer : IResponseRouter
4+
{
5+
6+
}
7+
}

src/JsonRpc/InputHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ private void HandleRequest(string request)
158158
}
159159
else if (response is ServerError serverError)
160160
{
161-
tcs.SetException(new Exception(_serializer.SerializeObject(serverError.Error)));
161+
tcs.SetException(new JsonRpcException(serverError));
162162
}
163163
}
164164

src/JsonRpc/JsonRpc.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<RootNamespace>OmniSharp.Extensions.JsonRpc</RootNamespace>
77
</PropertyGroup>
88
<ItemGroup>
9+
<PackageReference Include="System.Reactive" Version="$(System_Reactive_Version)" />
10+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(Microsoft_Extensions_DependencyInjection_Version)" />
11+
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(Microsoft_Extensions_Logging_Version)" />
912
<PackageReference Include="MediatR" Version="$(MediatR_Version)" />
1013
<PackageReference Include="Newtonsoft.Json" Version="$(Newtonsoft_Version)" />
1114
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(Microsoft_Extensions_Logging_Version)" />

src/JsonRpc/JsonRpcServer.cs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reactive.Linq;
6+
using System.Reactive.Subjects;
7+
using System.Reactive.Threading.Tasks;
8+
using System.Reflection;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using MediatR;
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.DependencyInjection.Extensions;
14+
using Microsoft.Extensions.Logging;
15+
using Newtonsoft.Json.Linq;
16+
using OmniSharp.Extensions.JsonRpc;
17+
18+
namespace OmniSharp.Extensions.JsonRpc
19+
{
20+
21+
public class JsonRpcServer : IJsonRpcServer
22+
{
23+
private readonly Connection _connection;
24+
private readonly IRequestRouter _requestRouter;
25+
private readonly IReciever _reciever;
26+
private readonly ISerializer _serializer;
27+
private readonly HandlerCollection _collection;
28+
private readonly IResponseRouter _responseRouter;
29+
private readonly IServiceProvider _serviceProvider;
30+
31+
public static Task<IJsonRpcServer> From(Action<JsonRpcServerOptions> optionsAction)
32+
{
33+
var options = new JsonRpcServerOptions();
34+
optionsAction(options);
35+
return From(options);
36+
}
37+
38+
public static async Task<IJsonRpcServer> From(JsonRpcServerOptions options)
39+
{
40+
var server = new JsonRpcServer(
41+
options.Input,
42+
options.Output,
43+
options.Reciever,
44+
options.RequestProcessIdentifier,
45+
options.LoggerFactory,
46+
options.Serializer,
47+
options.Services,
48+
options.HandlerTypes.Select(x => x.Assembly)
49+
.Distinct().Concat(options.HandlerAssemblies)
50+
);
51+
52+
await server.Initialize();
53+
54+
return server;
55+
}
56+
57+
internal JsonRpcServer(
58+
Stream input,
59+
Stream output,
60+
IReciever reciever,
61+
IRequestProcessIdentifier requestProcessIdentifier,
62+
ILoggerFactory loggerFactory,
63+
ISerializer serializer,
64+
IServiceCollection services,
65+
IEnumerable<Assembly> assemblies)
66+
{
67+
var outputHandler = new OutputHandler(output, serializer);
68+
69+
services.AddLogging();
70+
_reciever = reciever;
71+
_serializer = serializer;
72+
_collection = new HandlerCollection();
73+
74+
services.AddSingleton<IOutputHandler>(outputHandler);
75+
services.AddSingleton(_collection);
76+
services.AddSingleton(_serializer);
77+
services.AddSingleton<OmniSharp.Extensions.JsonRpc.ISerializer>(_serializer);
78+
services.AddSingleton(requestProcessIdentifier);
79+
services.AddSingleton(_reciever);
80+
services.AddSingleton(loggerFactory);
81+
82+
services.AddJsonRpcMediatR(assemblies);
83+
services.AddSingleton<IJsonRpcServer>(this);
84+
services.AddSingleton<IRequestRouter, RequestRouter>();
85+
services.AddSingleton<IReciever, Reciever>();
86+
services.AddSingleton<IResponseRouter, ResponseRouter>();
87+
88+
var foundHandlers = services
89+
.Where(x => typeof(IJsonRpcHandler).IsAssignableFrom(x.ServiceType) && x.ServiceType != typeof(IJsonRpcHandler))
90+
.ToArray();
91+
92+
// Handlers are created at the start and maintained as a singleton
93+
foreach (var handler in foundHandlers)
94+
{
95+
services.Remove(handler);
96+
97+
if (handler.ImplementationFactory != null)
98+
services.Add(ServiceDescriptor.Singleton(typeof(IJsonRpcHandler), handler.ImplementationFactory));
99+
else if (handler.ImplementationInstance != null)
100+
services.Add(ServiceDescriptor.Singleton(typeof(IJsonRpcHandler), handler.ImplementationInstance));
101+
else
102+
services.Add(ServiceDescriptor.Singleton(typeof(IJsonRpcHandler), handler.ImplementationType));
103+
}
104+
105+
_serviceProvider = services.BuildServiceProvider();
106+
107+
_requestRouter = _serviceProvider.GetRequiredService<IRequestRouter>();
108+
_responseRouter = _serviceProvider.GetRequiredService<IResponseRouter>();
109+
_connection = ActivatorUtilities.CreateInstance<Connection>(_serviceProvider, input);
110+
}
111+
112+
public IDisposable AddHandler(string method, IJsonRpcHandler handler)
113+
{
114+
return _collection.Add(method, handler);
115+
}
116+
117+
public IDisposable AddHandlers(params IJsonRpcHandler[] handlers)
118+
{
119+
return _collection.Add(handlers);
120+
}
121+
122+
public IDisposable AddHandler(string method, Type handlerType)
123+
{
124+
return _collection.Add(method, ActivatorUtilities.CreateInstance(_serviceProvider, handlerType) as IJsonRpcHandler);
125+
}
126+
127+
public IDisposable AddHandler<T>()
128+
where T : IJsonRpcHandler
129+
{
130+
return AddHandlers(typeof(T));
131+
}
132+
133+
public IDisposable AddHandlers(params Type[] handlerTypes)
134+
{
135+
return _collection.Add(
136+
handlerTypes
137+
.Select(handlerType => ActivatorUtilities.CreateInstance(_serviceProvider, handlerType) as IJsonRpcHandler)
138+
.ToArray());
139+
}
140+
141+
private async Task Initialize()
142+
{
143+
await Task.Yield();
144+
_connection.Open();
145+
}
146+
147+
public void SendNotification(string method)
148+
{
149+
_responseRouter.SendNotification(method);
150+
}
151+
152+
public void SendNotification<T>(string method, T @params)
153+
{
154+
_responseRouter.SendNotification(method, @params);
155+
}
156+
157+
public Task<TResponse> SendRequest<T, TResponse>(string method, T @params)
158+
{
159+
return _responseRouter.SendRequest<T, TResponse>(method, @params);
160+
}
161+
162+
public Task<TResponse> SendRequest<TResponse>(string method)
163+
{
164+
return _responseRouter.SendRequest<TResponse>(method);
165+
}
166+
167+
public Task SendRequest<T>(string method, T @params)
168+
{
169+
return _responseRouter.SendRequest(method, @params);
170+
}
171+
172+
public TaskCompletionSource<JToken> GetRequest(long id)
173+
{
174+
return _responseRouter.GetRequest(id);
175+
}
176+
177+
public void Dispose()
178+
{
179+
_connection?.Dispose();
180+
}
181+
}
182+
}

src/JsonRpc/JsonRpcServerOptions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace OmniSharp.Extensions.JsonRpc
9+
{
10+
public class JsonRpcServerOptions
11+
{
12+
public JsonRpcServerOptions()
13+
{
14+
}
15+
16+
public Stream Input { get; set; }
17+
public Stream Output { get; set; }
18+
public ILoggerFactory LoggerFactory { get; set; } = new LoggerFactory();
19+
public ISerializer Serializer { get; set; } = new Serializer();
20+
public IRequestProcessIdentifier RequestProcessIdentifier { get; set; } = new ParallelRequestProcessIdentifier();
21+
public IReciever Reciever { get; set; } = new Reciever();
22+
public IServiceCollection Services { get; set; } = new ServiceCollection();
23+
internal List<Type> HandlerTypes { get; set; } = new List<Type>();
24+
internal List<Assembly> HandlerAssemblies { get; set; } = new List<Assembly>();
25+
}
26+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace OmniSharp.Extensions.JsonRpc
8+
{
9+
public static class JsonRpcServerOptionsExtensions
10+
{
11+
public static JsonRpcServerOptions WithInput(this JsonRpcServerOptions options, Stream input)
12+
{
13+
options.Input = input;
14+
return options;
15+
}
16+
17+
public static JsonRpcServerOptions WithOutput(this JsonRpcServerOptions options, Stream output)
18+
{
19+
options.Output = output;
20+
return options;
21+
}
22+
23+
public static JsonRpcServerOptions WithLoggerFactory(this JsonRpcServerOptions options, ILoggerFactory loggerFactory)
24+
{
25+
options.LoggerFactory = loggerFactory;
26+
return options;
27+
}
28+
29+
public static JsonRpcServerOptions WithRequestProcessIdentifier(this JsonRpcServerOptions options, IRequestProcessIdentifier requestProcessIdentifier)
30+
{
31+
options.RequestProcessIdentifier = requestProcessIdentifier;
32+
return options;
33+
}
34+
35+
public static JsonRpcServerOptions WithSerializer(this JsonRpcServerOptions options, ISerializer serializer)
36+
{
37+
options.Serializer = serializer;
38+
return options;
39+
}
40+
41+
public static JsonRpcServerOptions WithReciever(this JsonRpcServerOptions options, IReciever reciever)
42+
{
43+
options.Reciever = reciever;
44+
return options;
45+
}
46+
47+
public static JsonRpcServerOptions WithHandler<T>(this JsonRpcServerOptions options)
48+
where T : class, IJsonRpcHandler
49+
{
50+
options.Services.AddSingleton<IJsonRpcHandler, T>();
51+
return options;
52+
}
53+
54+
public static JsonRpcServerOptions WithHandler<T>(this JsonRpcServerOptions options, T handler)
55+
where T : IJsonRpcHandler
56+
{
57+
options.Services.AddSingleton<IJsonRpcHandler>(handler);
58+
return options;
59+
}
60+
61+
public static JsonRpcServerOptions WithHandlersFrom(this JsonRpcServerOptions options, Type type)
62+
{
63+
options.HandlerTypes.Add(type);
64+
return options;
65+
}
66+
67+
public static JsonRpcServerOptions WithHandlersFrom(this JsonRpcServerOptions options, TypeInfo typeInfo)
68+
{
69+
options.HandlerTypes.Add(typeInfo.AsType());
70+
return options;
71+
}
72+
73+
public static JsonRpcServerOptions WithHandlersFrom(this JsonRpcServerOptions options, Assembly assembly)
74+
{
75+
options.HandlerAssemblies.Add(assembly);
76+
return options;
77+
}
78+
79+
public static JsonRpcServerOptions WithServices(this JsonRpcServerOptions options, Action<IServiceCollection> servicesAction)
80+
{
81+
servicesAction(options.Services);
82+
return options;
83+
}
84+
}
85+
}
Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
using System;
2+
using Newtonsoft.Json.Linq;
23

34
namespace OmniSharp.Extensions.JsonRpc.Server
45
{
56
public class JsonRpcException : Exception
67
{
7-
public JsonRpcException()
8+
public JsonRpcException(ServerError error)
89
{
10+
Error = error.Error;
11+
Id = error.Id;
12+
ProtocolVersion = error.ProtocolVersion;
913
}
1014

11-
public JsonRpcException(string message) : base(message)
12-
{
13-
}
14-
15-
public JsonRpcException(string message, Exception innerException) : base(message, innerException)
16-
{
17-
}
18-
19-
//protected JsonRpcException(SerializationInfo info, StreamingContext context) : base(info, context)
20-
//{
21-
//}
15+
public JToken Error { get; }
16+
public object Id { get; }
17+
public string ProtocolVersion { get; }
2218
}
23-
}
19+
}

0 commit comments

Comments
 (0)