Mono WCF Rest Service With Multiple Contracts "no endpoints are defined in the configuration file" - rest

I want to host a WCF Rest Service with multiple contracts via mono each implemented in a separate partial class. I read many posts on similar issues, yet there was no solution for mono. I incorporated or at least tested all suggestions I could find and by now my code looks a lot like other solutions, yet does not work.
The application runs successfully on my local machine but throws an error once I deploy it via mono.
Service 'MyWebServiceEndpoint' implements multiple ServiceContract types, and no endpoints are defined in the configuration file.
Here is one of the endpoints with the contract. All the others are very much like this one. They all are a partial class MyWebServiceEndpoint implementing another contract.
namespace MyServer.MyEndPoints {
public partial class MyWebServiceEndpoint : INotificationEndpoint {
public string GetNotifications(int limit) {
// Do stuff
}
}
[ServiceContract]
public interface INotificationEndpoint {
[OperationContract]
[WebGet]
string GetNotifications(int limit);
}
}
My App.config looks like this. I removed the IP and port, as they are the server address.
<system.serviceModel>
<services>
<service name="MyServer.MyEndPoints.MyWebServiceEndpoint" behaviorConfiguration="WebService.EndPoint">
<host>
<baseAddresses>
<add baseAddress="http://ip:port>"/>
</baseAddresses>
</host>
<endpoint address="/message"
binding="webHttpBinding"
contract="MyServer.MyEndPoints.IMessageEndpoint"
behaviorConfiguration="WebBehavior"/>
<endpoint address="/music"
binding="webHttpBinding"
contract="MyServer.MyEndPoints.IMusicEndpoint"
behaviorConfiguration="WebBehavior"/>
<endpoint address="/notification"
binding="webHttpBinding"
contract="MyServer.MyEndPoints.INotificationEndpoint"
behaviorConfiguration="WebBehavior"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WebService.EndPoint">
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
I open the service in C# like this.
WebServiceHost = new WebServiceHost(typeof(MyWebServiceEndpoint));
WebServiceHost.Open();
The Error message I receive on mono is:
Unhandled Exception:
System.InvalidOperationException: Service 'MyWebServiceEndpoint' implements multiple ServiceContract
types, and no endpoints are defined in the configuration file. WebServiceHost can set up default
endpoints, but only if the service implements only a single ServiceContract. Either change the
service to only implement a single ServiceContract, or else define endpoints for the service
explicitly in the configuration file. When more than one contract is implemented, must add base
address endpoint manually
I hope you have some hints or someone knows how to solve the issue. Thank you already for reading up to here.

I am not familiar with Mono, Does the Mono support Webconfig file? I advise you to add the service endpoint programmatically.
class Program
{
/// <param name="args"></param>
static void Main(string[] args)
{
WebHttpBinding binding = new WebHttpBinding();
Uri uri = new Uri("http://localhost:21011");
using (WebServiceHost sh = new WebServiceHost(typeof(TestService),uri))
{
sh.AddServiceEndpoint(typeof(ITestService), binding, "service1");
sh.AddServiceEndpoint(typeof(IService), binding, "service2");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
HttpGetEnabled = true
};
sh.Description.Behaviors.Add(smb);
}
sh.Opened += delegate
{
Console.WriteLine("service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("service is closed");
};
sh.Open();
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebGet]
string GetData(int id);
}
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet]
string Test();
}
public class TestService : ITestService,IService
{
public string GetData(int id)
{
return $"{id},";
}
public string Test()
{
return "Hello " + DateTime.Now.ToString();
}
}
Result.
According to the official documentation, we had better not use Partial class.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/multiple-contracts
Besides, we could consider launching multiple service host for every service implemented class.
Feel free to let me know if the problem still exists.

Related

How to change SOAP Web service Endpoint URL and WSDL URL in Apache Camel

The requirement is SOAP WSDL Url and Endpoint URL must be different from each other. Im using Apache Camel and Apache CXF below is my blueprint but when i request at port 8043 there is no wsdl there when i change it to 9143 wsdl is there.
Need to expose wsdl on url: http://0.0.0.0:8043/Services/Interface/FSServices/FSServices.serviceagent?wsdl
Endpoint URL be: http://0.0.0.0:9143/Services/Interface/FSServices/FSServices.serviceagent/PortTypeEndpoint1/
<cxf:cxfEndpoint
address="http://0.0.0.0:8043/Services/Interface/FSServices/FSServices.serviceagent"
id="fsEndpoint" serviceClass="pk.com.herman.fs.operation.PortType">
<cxf:properties>
<entry key="publishedEndpointUrl" value="http://0.0.0.0:9143/Services/Interface/FSServices/FSServices.serviceagent/PortTypeEndpoint1/"/>
</cxf:properties>
</cxf:cxfEndpoint>
Weird requirement. You could do this by adding an interceptor which disables the WSDLGetInterceptor interceptor.
Add Interceptor
<bean id="removeWSDLinterceptor"
class="my.package.RemoveWSDLInterceptor" />
<cxf:cxfEndpoint address="http://0.0.0.0:8043/Services/Interface/FSServices/FSServices.serviceagent"
id="fsEndpoint" serviceClass="pk.com.herman.fs.operation.PortType">
<cxf:inInterceptors>
<ref bean="removeWSDLinterceptor" />
</cxf:inInterceptors>
</cxf:cxfEndpoint>
Interceptor
public class RemoveWSDLInterceptor extends AbstractPhaseInterceptor<Message>
{
public RemoveWSDLInterceptor() {
super(Phase.RECEIVE);
}
public void handleMessage(Message message) {
WSDLGetInterceptor getWSDLInterceptor = null;
InterceptorChain chain = message.getInterceptorChain();
for(Iterator<Interceptor<? extends Message>> iter = chain.iterator(); iter.hasNext();) {
Interceptor getWSDLInterceptor = iter.next();
if (interceptor instanceof WSDLGetInterceptor) {
getWSDLInterceptor = (WSDLGetInterceptor) interceptor;
}
}
chain.remove(getWSDLInterceptor);
}
public void handleFault(Message messageParam) {
}
}
And then you can add a small jetty route to return the WSDL statically.
<route>
<from uri="jetty://http://0.0.0.0:9143" />
<to uri="language:constant:resource:file:/path/to/your/wsdlfile.wsdl"/>
</route>

intercept-url patterns for REST URLs

I am designing a set of REST APIs for accessing the URLs. As per my requirement there are two URLs:
http://localhost:3126/securitydemo/webapi/db/students
to view all the students no access required and
http://localhost:3126/securitydemo/webapi/db/students/1
only ROLE_USER are allowed.
My Spring Security configuration:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="**/students/*" access="hasRole('ROLE_USER')" />
<http-basic/>
</http>
If I use the pattern **/students/* no basic security popup occurs. If I use /** it's working properly.
How can I intercept the two URLs with different security levels?
My REST service class:
#Path("/db")
#Produces(MediaType.APPLICATION_JSON)
public class StudentService {
static StudentDao data = new StudentDaoImpl();
#Path("/students")
#GET
public Response getStudents(){
GenericEntity<List<Student>> entity = new GenericEntity<List<Student>>(data.getAllStudents()){};
return Response.ok(entity).build();
}
#Path("/students/{id}")
#GET
public Response getStudent(#PathParam("id") int id){
return Response.ok(data.getStudent(id)).build();
}
}
try this
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/securitydemo/webapi/db/students/1/**" access="hasRole('ROLE_USER')" />
<http-basic/>
</http>

Symfony DIC and Parent Services not working

I'm integrating Symfony DIC in a zend framework application, that's going fine except for parent services.
In my DIC config I have a parent service PC_Service which will be extended by all my services.
The problem is that the entity manager is not available (NULL) in the services that extend PC_Service. When I inject the entitymanager via service.stats the entitymanger is set correctly.
...
<service id="pc.service" class="PC_Service" abstract="true">
<call method="setEntityManager">
<argument type="service" id="doctrine.entitymanager" />
</call>
</service>
...
<service id="service.stats" class="Application_Service_Stats" parent="pc.service" />
...
PC_Service
abstract class PC_Service
{
protected $_em;
public function setEntityManager($entityManager)
{
$this->_em = $entityManager;
}
}
Application_Service_Stats
class Application_Service_Stats extends PC_Service
{
... $this->_em should be set here.
}
I hope someone can tell me what I'm doing wrong.
Don't know if it's a typo but it should be doctrine.orm.default_entity_manager or doctrine.orm.entity_manager (alias of the previuos):
<service id="pc.service" class="PC_Service" abstract="true">
<call method="setEntityManager">
<argument type="service" id="doctrine.orm.default_entity_manager" />
</call>
</service>
The solution is to compile the service container near the end of the ZF bootstrap. This process has a step called ResolveDefinitionTemplatesPass which patches in the calls from parent services.
This is typically done by the Symfony Kernel, but of course it isn't present in a ZF integration.
protected function _initServiceContainerCompilation()
{
// Wait for the SC to get built
$this->bootstrap('Services');
// Doctrine modifies the SC, so we need to wait for it also
$this->bootstrap('Doctrine');
// Compiling the SC allows "ResolveDefinitionTemplatesPass" to run,
// allowing services to inherit method calls from parents
$sc = $this->getResource('Services');
$sc->compile();
}

Configuring gwt-log's remoteLogger; use log4j to put it in a separate file

I have a (Smart)GWT application, that uses Spring on the server-side, and logs its stuff there via log4j. This works (deploying on tomcat6/ubuntu 10.04 LTS).
On the client-side I use the gwt-log remote logging library, configured properly. When running debug mode, I see the gwt-logs in the Eclipse 'Development Mode' pane. When deployed however, I don't see the gwt-log logs. I have configured things as follows:
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
...
<appender name="FILE_LOG2" class="org.apache.log4j.FileAppender">
<param name="File" value="${PuzzelVandaag-instance-root}WEB-INF/logs/Sytematic.log" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="--- %d [%.4t] %-5p %c{1} - %m%n"/>
</layout>
</appender>
...
<!-- this one works, normal server-side code -->
<category name="com.isomorphic">
<priority value="DEBUG" />
<appender-ref ref="FILE_LOG2" />
</category>
<!-- currently I use this to configure gwt-log stuff. Is this the right way? -->
<category name="gwt-log">
<level value="DEBUG" />
<appender-ref ref="FILE_LOG2"/>
</category>
The server-side package logging works, but I have troubles with the client-side. I am fairly sure the remote logging servlet works, as I don't see any errors on this. I have it configured as follows, in web.xml:
<servlet>
<servlet-name>gwt-log-remote-logger-servlet</servlet-name>
<servlet-class>com.allen_sauer.gwt.log.server.RemoteLoggerServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gwt-log-remote-logger-servlet</servlet-name>
<url-pattern>/[modulename]/gwt-log</url-pattern>
</servlet-mapping>
When I log stuff, I do a call like Log.debug("some msg"), whilst importing com.allen_sauer.gwt.log.client.Log.
All-in-all I think I followed the correct approach. I also run hosted mode with the -Dlog4j.debug parameter, and this is what it tells me:
log4j: Retreiving an instance of org.apache.log4j.Logger.
log4j: Setting [gwt-log] additivity to [true].
log4j: Level value for gwt-log is [DEBUG].
log4j: gwt-log level set to DEBUG
log4j: Adding appender named [STDOUT] to category [gwt-log].
log4j: Adding appender named [SmartClientLog] to category [gwt-log].
log4j: Adding appender named [FILE_LOG2] to category [gwt-log].
For completion, here is the relevant part of .gwt.xml:
<inherits name="com.allen_sauer.gwt.log.gwt-log-DEBUG"/>
<set-property name="log_DivLogger" value="DISABLED"/>
<!-- In gwt-log-3.0.3 or later -->
<inherits name="com.allen_sauer.gwt.log.gwt-log-RemoteLogger"/>
Am I missing something obvious? I am a log4j newbie... Any help would be greatly appreciated!
If you take a look at the com.google.gwt.logging.server.RemoteLoggingServiceImpl code you will see that it is using java.util.logging.Logger to perform it's logging.
You are using Log4j.
There are two options for getting your logs to appear in Log4j.
Implement your own RemoteLoggingService
Use slf4j to "bridge" java.util.logging with log4j logging
Option 1 is not too hard.
I have below the class I created for this. Remember to point your web.xml to this new class.
import java.util.logging.LogRecord;
import com.google.gwt.logging.shared.RemoteLoggingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import java.util.logging.Level;
import org.springframework.stereotype.Component;
public class MyRemoteLoggingServlet extends RemoteServiceServlet implements RemoteLoggingService {
private final MyLogger logger = MyLoggerFactory.getLogger(getClass());
#Override
public String logOnServer(LogRecord record) {
Level level = record.getLevel();
String message = record.getMessage();
if (Level.INFO.equals(level)) {
logger.info(message);
} else if (Level.SEVERE.equals(level)) {
logger.error(message);
} else if (Level.WARNING.equals(level)) {
logger.warn(message);
} else if (Level.FINE.equals(level)) {
logger.debug(message);
}
return null;
}
}
Option 2
In this option you use SLF4J for your logging and configure a bridge that will redirect the java.util.logging.Logger to Log4j.
I havent implemented this method myself, but you can read about it here:
JUL to SLF4J Bridge
I took this approach, works for me.
public class UILogging extends RemoteServiceServlet implements
RemoteLoggingService {
private static final String SYMBOL_MAPS = "symbolMaps";
private static StackTraceDeobfuscator deobfuscator = null;
private static Logger logger = Logger.getLogger(UILogging.class);
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
setSymbolMapsDirectory(config.getInitParameter(SYMBOL_MAPS));
}
/**
* Logs a Log Record which has been serialized using GWT RPC on the server.
*
* #return either an error message, or null if logging is successful.
*/
public final String logOnServer(LogRecord lr) {
String strongName = getPermutationStrongName();
try {
if (deobfuscator != null) {
lr = deobfuscator.deobfuscateLogRecord(lr, strongName);
}
if (lr.getLevel().equals(Level.SEVERE)) {
logger.error(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.INFO)) {
logger.info(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.WARNING)) {
logger.warn(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.FINE)) {
logger.debug(lr.getMessage(),lr.getThrown());
} else if (lr.getLevel().equals(Level.ALL)) {
logger.trace(lr.getMessage(),lr.getThrown());
}
} catch (Exception e) {
logger.error("Remote logging failed", e);
return "Remote logging failed, check stack trace for details.";
}
return null;
}
/**
* By default, this service does not do any deobfuscation. In order to do
* server side deobfuscation, you must copy the symbolMaps files to a
* directory visible to the server and set the directory using this method.
*
* #param symbolMapsDir
*/
public void setSymbolMapsDirectory(String symbolMapsDir) {
if (deobfuscator == null) {
deobfuscator = new StackTraceDeobfuscator(symbolMapsDir);
} else {
deobfuscator.setSymbolMapsDirectory(symbolMapsDir);
}
}
}

How to expose service contract interfaces with multiple inheritance in WCF service on single endpoint

I have only simple data types in method signature of service (such as int, string). My service class implements single ServiceContract interface say IMathService, and this interface in turn inherits from some other base interface say IAdderService. I want to expose the MathService using interface contract IAdderService as a service on a single endpoint. However some of the clinet's which know about IMathService should be able to access the extra services provided by IMathService on that single endpoint i.e. by just typecasting IAdderService to IMathService.
//Interfaces and classes at server side
[ServiceContract]
public interface IAdderService
{
[OperationContract]
int Add(int num1, int num2);
}
[ServiceContract]
public interface IMathService : IAdderService
{
[OperationContract]
int Substract(int num1, int num2);
}
public class MathService : IMathService
{
#region IMathService Members
public int Substract(int num1, int num2)
{
return num1 - num2;
}
#endregion
#region IAdderService Members
public int Add(int num1, int num2)
{
return num1 + num2;
}
#endregion
}
//Run WCF service as a singleton instace
MathService mathService = new MathService();
ServiceHost host = new ServiceHost(mathService);
host.Open();
Server side Configuration:
<configuration>
<system.serviceModel>
<services>
<service name="IAdderService"
behaviorConfiguration="AdderServiceServiceBehavior">
<endpoint address="net.pipe://localhost/AdderService"
binding="netNamedPipeBinding"
bindingConfiguration="Binding1"
contract="TestApp.IAdderService" />
<endpoint address="mex"
binding="mexNamedPipeBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/AdderService"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netNamedPipeBinding>
<binding name="Binding1" >
<security mode = "None">
</security>
</binding >
</netNamedPipeBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="AdderServiceServiceBehavior">
<serviceMetadata />
<serviceDebug includeExceptionDetailInFaults="True" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client Side imeplementation:
IAdderService adderService = new
ChannelFactory<IAdderService>("AdderService").CreateChannel();
int result = adderService.Add(10, 11);
IMathService mathService = adderService as IMathService;
result = mathService.Substract(100, 9);
Client side configuration:
<configuration>
<system.serviceModel>
<client>
<endpoint name="AdderService" address="net.pipe://localhost/AdderService" binding="netNamedPipeBinding" bindingConfiguration="Binding1" contract="TestApp.IAdderService" />
</client>
<bindings>
<netNamedPipeBinding>
<binding name="Binding1" maxBufferSize="65536" maxConnections="10">
<security mode = "None">
</security>
</binding >
</netNamedPipeBinding>
</bindings>
</system.serviceModel>
</configuration>
Using above code and configuration I am not able to typecast IAdderService instnace to IMathService, it fails and I get null instance of IMathService at client side.
My observation is if server exposes IMathService to client then client can safely typecast to IAdderService and vice versa is also possible. However if server exposes IAdderService then the typecast fails.
Is there any solution to this? or am I doing it in a wrong way.
Just as you observed, you have to expose the derived service (MathService) instead of the AdderService.
WCF has no idea that AdderService happens to actually be a MathService implementing a MathService interface. It is regarded as AdderService - so there is no way to cast it to a derived class.
I replicated your situation exactly except I used an IMathService endpoint rather than an IAdderService endpoint.
IAdderService adderService = new ChannelFactory<IMathService>("MathService").CreateChannel();
int result = adderService.Add(10, 11);
IMathService mathService = adderService as IMathService;
result = mathService.Substract(100, 9);
This works for me. You can't cast a base type to a derived type. You can do the reverse however.