Exporting several services using Service Component Runtime - osgi

For example i have component exporting simple web-service:
#Component(
property = ["osgi.jaxrs.resource=true"],
configurationPid = ["some.file.config"]
)
#Path("/service")
open class Service {
#GET
#Produces("text/plain")
#Path("/getData")
fun getData(): Response = Response.ok("Some data").build()
#POST
#Consumes("text/plain")
#Path("/setData")
fun registered(inputData: String): Response = Response.ok().build()
}
Is creating another component only way to publish another service ? Can i do this somehow using single component ?

Related

Rest helper class instances best practice?

I'm new to rest webservice and have a novice q'.
I've created a rest class and would like to use helper classes to handle certain operations. So for example I've created the following service:
import statements...
#Path("/UserResources")
public class UserResource {
//Create Spring application context
static ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext("classpath:/spring.xml");
private UserResourceHelper urh = new UserResourceHelper(); // this is the helper
class
UserProfileService userProfileService = ctx.getBean(UserProfileService.class);
#POST
#Path("/createUser")
#Consumes(MediaType.APPLICATION_JSON)
public Response createUser(#Context HttpServletRequest request, Object object) {
StringBuffer sb = new StringBuffer();
User user = userProfileService.findByPrimaryKey(object);
sb.append(urh.createUser(object));
return
Response.status(Status.CREATED.getStatusCode()).entity(result.toString()).build();
}
}
I have a couple of questions on this approach:
Is this the correct way to instantiate the helper class? Or should I create a constructor and instantiate the class there? for example:
public UserResource (){
urh = new UserResourceHelper();
}
On this approach will there always be a new instance of the
UserResourceHelper?
If so that would mean there will not be an issue
on concurrency correct? ie. 2 requests coming in at the same time
and the 1st object being received by the createUser method would
suddenly get replaced by the 2nd object that suddenly came in?
I'm using Hibernate for ORM. Is the way i've instantiated the entities as per my code sample correct?
Thanks for your assistance!

Inject service with different configuration into controller

In a Web API application, I have two controllers, MyAController and MyBController, each depending on IMyService but with a different configuration:
public class MyAController : ApiController
{
private readonly IMyService service;
public MyAController(IMyService service)
{
this.service = service;
}
}
public class MyBController : ApiController
{
private readonly IMyService service;
public MyBController(IMyService service)
{
this.service = service;
}
}
public interface IMyService
{
}
public class MyService : IMyService
{
private readonly string configuration;
public MyService(string configuration)
{
this.configuration = configuration;
}
}
I've tried configuring DryIoc the following way:
private enum ServiceKeyEnum
{
ServiceA,
ServiceB
}
container.RegisterInstance("configurationA", serviceKey: "CONFIGURATIONA");
container.RegisterInstance("configurationB", serviceKey: "CONFIGURATIONB");
container.Register<IMyService, MyService>(Reuse.Singleton, Made.Of(() => new MyService(Arg.Of<string>("CONFIGURATIONA"))), serviceKey: ServiceKeyEnum.ServiceA);
container.Register<IMyService, MyService>(Reuse.Singleton, Made.Of(() => new MyService(Arg.Of<string>("CONFIGURATIONB"))), serviceKey: ServiceKeyEnum.ServiceB);
container.Register<MyAController>(Reuse.InResolutionScope, made: Parameters.Of.Details((r, p) => ServiceDetails.IfUnresolvedReturnDefault).Type<IMyService>(serviceKey: ServiceKeyEnum.ServiceA));
container.Register<MyBController>(Reuse.InResolutionScope, made: Parameters.Of.Details((r, p) => ServiceDetails.IfUnresolvedReturnDefault).Type<IMyService>(serviceKey: ServiceKeyEnum.ServiceB));
and if I try to call resolve using:
var controllerA = container.Resolve<MyAController>();
var controllerB = container.Resolve<MyBController>();
I get two controllers configured with configurationA and configurationB respectively.
However, when I try to call the api using a REST call, I get the following error:
An error occurred when trying to create a controller of type 'MyAController'. Make sure that the controller has a parameterless public constructor.
so I guess, that I need to register the controller in a different way... but how?
Any help would be greatly appreciated....
The error is caused by improper setup for controllers. The DryIoc.WebApi extension have already discovered and registered your controllers, so normally you don't need to do it yourself. I will provide the working code (from the question comments) for you specific setup later. But now the reason behind the "parameterless constructor..": when DryIoc fails, WebAPI falls back to using Activator.CreateInstance for controller, which expects parameterless constructor. The fallback masks the original DryIoc error. To find it, you can setup DryIoc.WebApi extension as:
container = container.WithWebApi(throwIfUnresolved: type => type.IsController());
The working setup for your case, which registers dependencies with condition to select controller for injection:
container.Register<IMyService, MyService>(Made.Of(
() => new MyService(Arg.Index<string>(0)), _ => "configurationA"),
Reuse.Singleton,
setup: Setup.With(condition: r => r.Parent.ImplementationType == typeof(MyAController)));
container.Register<IMyService, MyService>(Made.Of(
() => new MyService(Arg.Index<string>(0)), _ => "configurationB"),
Reuse.Singleton,
setup: Setup.With(condition: r => r.Parent.ImplementationType == typeof(MyBController)));
The main thing that this setup does not require special controller registration.
Plus you can avoid using service keys, and no need to register config strings separately.

Using Scala classes as DTOs in Spring MVC

In my project I'm using Spring + Scala.
Some of my Spring MVC controllers uses Spring feature for binding incoming HTTP parameters to DTO object. Like this:
#RequestMapping(value = Array("/", ""), method = Array(RequestMethod.POST))
def saveProduct(dto: MyDto): Iterable[MyDto] = {...}
And MyDto is simple scala class:
class MyDto extends Serializable {
#BeanProperty var id : Long = _
#BeanProperty var name: String = _
}
My problem is that I'm getting exceptions when trying to use Scala Option class for fields in MyDto:
class MyDto extends Serializable {
#BeanProperty var id : Option[Long] = None
#BeanProperty var name: Option[String] = None
}
Exception message is:
Failed to convert property value of type 'java.lang.String' to required type 'scala.Option' for property 'name';
What I can do to use Scala Options as type if fields in MyDto?
I am not a Scala expert, but here is one way:
Create a converter, along these lines:
import org.springframework.core.convert.converter.Converter
class TypeToOptionOfTypeConverter[T] extends Converter[T, Option[T]] {
override def convert(source: T): Option[T] = {
Some(source)
}
}
Register this converter with Spring MVC:
class WebConfig extends WebMvcConfigurerAdapter {
override def addFormatters(registry: FormatterRegistry): Unit = {
registry.addConverter(classOf[String], classOf[Option[String]], new TypeToOptionOfTypeConverter[String])
registry.addConverter(classOf[Long], classOf[Option[Long]], new TypeToOptionOfTypeConverter[Long])
}
}
That should be it, now your DTO should get cleanly mapped.
Spring has support for converting types using converters with its data binding. You will need to implement the converter so that Spring knows how to convert, for example, String to Option[String].
See:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert

AEM:OSGI sling service activate method not being executed

I am trying to create a very basic sling service in AEM:
package com.mypackage;
/**
* A simple service interface
*/
public interface TestService {
/**
* #return the name of the underlying JCR repository implementation
*/
public String getPropertyName();
}
The implementation class:
package com.mymypackage.impl;
import javax.jcr.Repository;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.jcr.api.SlingRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mypackage.TestService;
#Component(label = "Title", description = "Description.", immediate = true, metatype = true, policy = ConfigurationPolicy.REQUIRE)
#Service(value = {TestService.class})
#Properties({
#Property(name = "propertyPath", label = "Property Label", description = "Property Desc.")
})
public class TestServiceImpl implements TestService {
private static final Logger log = LoggerFactory.getLogger(TestServiceImpl.class);
String propertyPath = null;
#Activate
public void activate(ComponentContext ctx) {
Dictionary properties = ctx.getProperties();
String propertyPath =(String)properties.get("propertyPath");
log.info("====================getPropertyName activate========================"+propertyPath);
this.propertyPath = propertyPath;
}
#Override
public String getPropertyName() {
// TODO Auto-generated method stub
log.info("====================getPropertyName========================"+propertyPath);
return propertyPath;
}
}
and I have created a node of type sling:OsgiConfig inside the config folder. The name of this node is com.mypackage.impl.TestServiceImpl.xml and are the content of it:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
propertyPath="http://www.myuhc.com"/>
and this is how I am trying to use it inside a Java class:
public static String getTestService() {
TestService testService = new TestServiceImpl();
String prop = testService.getPropertyName();
return prop;
}
This method is being called from a JSP using customtaglib (method being mapped through a .tld file)
When I use this approach the activate method inside the HelloServiceImpl class is not called and hence the property is not set. But when I use the service inside a component JSP like this:
<%#page import="org.osgi.service.cm.ConfigurationAdmin"%>
<%#page import="org.osgi.service.cm.Configuration"%>
<%
Configuration conf = sling.getService(org.osgi.service.cm.ConfigurationAdmin.class).getConfiguration("Name of the config");
String myProp = (String) conf.getProperties().get("property key");
%>
everything works fine. There is something really wrong I must be doing while trying to call the service from a Java class. How can I use that approach. I don't want to use scriptlet inside JSP. Also, any best practices would be highly appreciated.
Thanks in advance
OSGi Services work within a life cycle which manages the creation and deletion of the Service instances.
As part of the container management of these instances, when the container creates the instance it calls the activation method with the appropriate values. So that the property can be assigned.
What you are doing in your first code snippet:
TestService testService = new TestServiceImpl();
String prop = testService.getPropertyName();
Is not using the containers version of your component. Your using the direct implementation and bypassing the container management.
To use the instance that is managed by the container. You need to request it from the container.
Which is exactly what your second snippet shows
sling.getService(org.osgi.service.cm.ConfigurationAdmin.class)
Is requesting the best matching service from the container.
To access a service from the container. You either need to be a Service yourself. Which you can do by the #Reference annotation.
You mentioned however being in a taglib which makes things a bit more complicated. You need to obtain a reference to the SlingScriptHelper which can be obtained from the pageContext like this;
ServletRequest request = pageContext.getRequest();
final SlingBindings bindings = (SlingBindings) request
.getAttribute(SlingBindings.class.getName());
final SlingScriptHelper scriptHelper = bindings.getSling();
TestService service= scriptHelper
.getService(com.mymypackage.impl.TestService.class);
The problem is, that sometimes when you try to re-install a bundle, some old compiled classess are not removed and replaced with a new one. Try to remove the /var/classes and/or /var/clientlibs nodes and re-install your projects.
This may also be useful, if you are unable to use #Reference annotation for injecting your service
// get the TestServiceImpl.java service component
TestServiceImpl testService = getSlingScriptHelper().getService(TestServiceImpl.class);
//com.adobe.cq.sightly.WCMUsePojo.getSlingScriptHelper()

Spring web service maven-jaxb2-plugin complex input

I need to create a client based on WSDL. I am using Spring 4 and Spring web service 2.2.0. Using maven-jaxb2-plugin I created classes and able to invoke web service. I am using object factory to create some objects, due to complex data type. Now I have a complex input. and I don't find appropriate methods in the generated class.
As per SOAP UI , the request body looks like
<soapenv:Body>
<zhin:ZHIN_HR_CA_EVALUATOR_SEND_RFCresponse>
<IN_DATA>
<IV_PERNR>4005002</IV_PERNR>
<IT_RATERS>
<!--1 or more repetitions:-->
<ArrayOfIT_RATERSItem>
<ORGTX>?</ORGTX>
<DESIG>?</DESIG>
<ENAME>N V S Ravi Kumar</ENAME>
<ZCPERNRT>?</ZCPERNRT>
<PERNR>4005001</PERNR>
<WEIGH>?</WEIGH>
<SEQUENCE>1St Evaluator</SEQUENCE>
</ArrayOfIT_RATERSItem>
</IT_RATERS>
<IV_APCAT>1</IV_APCAT>
</IN_DATA>
</zhin:ZHIN_HR_CA_EVALUATOR_SEND_RFCresponse>
</soapenv:Body>
Now i don't find a method to set ArrayOfITRATERS but I have getArrayOfITRATERSItem()
final ObjectFactory objectFactory = new ObjectFactory();
final INDATA requestValue = objectFactory.createINDATA();
requestValue.setIVPERNR(String.valueOf(id));
requestValue.setIVAPCAT(AppConstants.CA);
final ArrayOfITRATERS value = objectFactory.createArrayOfITRATERS();
value.set .....(not found ???)
requestValue.setITRATERS(value);
My autogenerated ArrayOfITRATERS class looks like
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ArrayOfIT_RATERS", propOrder = {
"arrayOfITRATERSItem"
})
public class ArrayOfITRATERS {
#XmlElement(name = "ArrayOfIT_RATERSItem", required = true)
protected List<ITRATERS> arrayOfITRATERSItem;
public List<ITRATERS> getArrayOfITRATERSItem() {
if (arrayOfITRATERSItem == null) {
arrayOfITRATERSItem = new ArrayList<ITRATERS>();
}
return this.arrayOfITRATERSItem;
}
}
I am not sure how to set the ArrayOfITRATERS
Any help is appreciated.
Does
getArrayOfITRATERSItem().add(myITRATERS);
solve your problem?
JAXB XJC does not generate setters for collections by default. Just add your items to the collection returned by the getter.

Resources