Migrate felix annotation property final String into OSGI - osgi

Can someone write correct this migration to OSGI?
#Component(immediate = true, service = CustomExternalizer.class,
properties = {
"process.label=Custom Externalizer Service"
},
configurationPolicy = ConfigurationPolicy.REQUIRE)
#Designate(ocd = CustomExternalizerImpl.Config.class)
public class CustomExternalizerImpl implements CustomExternalizer {
// its felix => this need to OSGI
#Property(label = "Domains", unbounded = PropertyUnbounded.ARRAY,
description = "List of domain mappings. In the form: '<site root path> scheme://domain[:port]'."
+ "Standard required names are 'author' and 'publish'")
public static final String PROPERTY_EXTERNALIZER_DOMAINS = "externalizer.domains";
#ObjectClassDefinition
protected #interface Config {
#AttributeDefinition(
???????????
}
I try some approach, but have only mix

Related

java test custom annotation that load message from properties file

I have create custom annotation that load default error message from properties file
Java hibernate-validator #interface load from properties
now i want to test it, that when invalid value is fill, the default error is show up
here is my test class
public class CreditCardTest {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
String[] inValidValues = new String[] {
"??",
"##",
"]]]"
};
#Test
public void ccInValid() {
for (String value : inValidValues) {
AnnotatedBean bean = new AnnotatedBean();
bean.value = value;
Set<ConstraintViolation<AnnotatedBeanCustom>> constraintViolationCustom = validator.validate(bean);
constraintViolation.stream().forEach( v -> assertEquals(v.getMessage(), "contains invalid character"));
}
}
private class AnnotatedBean {
#AccountNumber
String value;
}
}
when i run above test class the error is like
org.junit.ComparisonFailure: expected:<[{pacakge.cc.message}]> but was:<[contains invalid character]>
how do make the annotation on test class load the properties file?

AEM 6.3 - Migrate Felix to OSGi annotations: How to deal with propertyPrivate?

I'm migrating an AEM 6.1 application to AEM 6.3. Since Felix annotations (org.apache.felix.scr.annotations.*) are deprecated, I decided to migrate my components to the OSGi annotations (org.osgi.service.component.annotations.*).
Once I figured out how it works, it is pretty easy. But there is one case I don't know how to handle: Properties with propertyPriavte = true.
The old implementation looks like this:
#Component(metatype = true)
#Service(Servlet.class)
#Properties({
#Property(name = "sling.servlet.selectors", value = "overlay", propertyPrivate = true),
})
public class OverlayServletImpl extends OverlayServlet {
...
}
The property sling.servlet.selectors would not be configurable in the Configuration Manager at the AEM console, but it would be configurable due to a config file, right? So, I still need to define this property.
For other properties I changed my implementation like this:
// OverlayServletImpl
#Component(
service = Servlet.class,
configurationPid = "my.package.path.OverlayServletImpl"
)
#Designate(
ocd = OverlayServletImplConfiguration.class
)
public class OverlayServletImpl extends OverlayServlet {
...
}
// Configuration
#ObjectClassDefinition(name = "Overlay Servlet")
public #interface OverlayServletImplConfiguration {
String sling_servlet_selectors() default "overlay";
...
}
Now, I have the property sling.servlet.selectors, but it is also available in Configuration Manager and it'S value can be changed there. But I don't want that.
How can I do that? Is this possible with the OSGi annotations?
Thank you and best regards!
It looks like this might be possible if you use the #Component annotation to specify your private properties.
#Component(service = Servlet.class,
property =
{ SLING_SERVLET_RESOURCE_TYPES + "=aemhtlexamples/structure/page",
SLING_SERVLET_METHODS + "=GET",
SLING_SERVLET_EXTENSIONS + "=html",
SLING_SERVLET_SELECTORS + "=hello" })
public class SimpleServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp)
throws ServletException, IOException {
final Resource resource = req.getResource();
resp.getOutputStream().println(resource.toString());
resp.getOutputStream().println("This content is generated by the SimpleServlet");
}
}
Source: https://github.com/heervisscher/htl-examples/blob/master/core/src/main/java/com/adobe/examples/htl/core/servlets/SimpleServlet.java
As far as I know this is not possible. Every property you define can be overridden by config.

Get a specific service implementation based on a parameter

In my Sling app I have data presenting documents, with pages, and content nodes. We mostly server those documents as HTML, but now I would like to have a servlet to serve these documents as PDF and PPT.
Basically, I thought about implementing the factory pattern : in my servlet, dependending on the extension of the request (pdf or ppt), I would get from a DocumentBuilderFactory, the proper DocumentBuilder implementation, either PdfDocumentBuilder or PptDocumentBuilder.
So first I had this:
public class PlanExportBuilderFactory {
public PlanExportBuilder getBuilder(String type) {
PlanExportBuilder builder = null;
switch (type) {
case "pdf":
builder = new PdfPlanExportBuilder();
break;
default:
logger.error("Unsupported plan export builder, type: " + type);
}
return builder;
}
}
In the servlet:
#Component(metatype = false)
#Service(Servlet.class)
#Properties({
#Property(name = "sling.servlet.resourceTypes", value = "myApp/document"),
#Property(name = "sling.servlet.extensions", value = { "ppt", "pdf" }),
#Property(name = "sling.servlet.methods", value = "GET")
})
public class PlanExportServlet extends SlingSafeMethodsServlet {
#Reference
PlanExportBuilderFactory builderFactory;
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
Resource resource = request.getResource();
PlanExportBuilder builder = builderFactory.getBuilder(request.getRequestPathInfo().getExtension());
}
}
But the problem is that in the builder I would like to reference other services to access Sling resources, and with this solution, they're not bound.
I looked at Services Factory with OSGi but from what I've understood, you use them to configure differently the same implementation of a service.
Then I found that you can get a specific implementation by naming it, or use a property and a filter.
So I've ended up with this:
public class PlanExportBuilderFactory {
#Reference(target = "(builderType=pdf)")
PlanExportBuilder pdfPlanExportBuilder;
public PlanExportBuilder getBuilder(String type) {
PlanExportBuilder builder = null;
switch (type) {
case "pdf":
return pdfPlanExportBuilder;
default:
logger.error("Unsupported plan export builder, type: " + type);
}
return builder;
}
}
The builder defining a "builderType" property :
// AbstractPlanExportBuilder implements PlanExportBuilder interface
#Component
#Service(value=PlanExportBuilder.class)
public class PdfPlanExportBuilder extends AbstractPlanExportBuilder {
#Property(name="builderType", value="pdf")
public PdfPlanExportBuilder() {
planDocument = new PdfPlanDocument();
}
}
I would like to know if it's a good way to retrieve my PDF builder implementation regarding OSGi good practices.
EDIT 1
From Peter's answer I've tried to add multiple references but with Felix it doesn't seem to work:
#Reference(name = "planExportBuilder", cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
private Map<String, PlanExportBuilder> builders = new ConcurrentHashMap<String, PlanExportBuilder>();
protected final void bindPlanExportBuilder(PlanExportBuilder b, Map<String, Object> props) {
final String type = PropertiesUtil.toString(props.get("type"), null);
if (type != null) {
this.builders.put((String) props.get("type"), b);
}
}
protected final void unbindPlanExportBuilder(final PlanExportBuilder b, Map<String, Object> props) {
final String type = PropertiesUtil.toString(props.get("type"), null);
if (type != null) {
this.builders.remove(type);
}
}
I get these errors :
#Reference(builders) : Missing method bind for reference planExportBuilder
#Reference(builders) : Something went wrong: false - true - MANDATORY_MULTIPLE
#Reference(builders) : Missing method unbind for reference planExportBuilder
The Felix documentation here http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin/scr-annotations.html#reference says for the bind method:
The default value is the name created by appending the reference name to the string bind. The method must be declared public or protected and take single argument which is declared with the service interface type
So according to this, I understand it cannot work with Felix, as I'm trying to pass two arguments. However, I found an example here that seems to match what you've suggested but I cannot make it work: https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/bundle/src/main/java/com/adobe/acs/samples/services/impl/SampleMultiReferenceServiceImpl.java
EDIT 2
Just had to move the reference above the class to make it work:
#References({
#Reference(
name = "planExportBuilder",
referenceInterface = PlanExportBuilder.class,
policy = ReferencePolicy.DYNAMIC,
cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE)
})
public class PlanExportServlet extends SlingSafeMethodsServlet {
Factories are evil :-) Main reason is of course the yucky class loading hacks that are usually used but also because they tend to have global knowledge. In general, you want to be able to add a bundle with a new DocumentBuilder and then that type should become available.
A more OSGi oriented solution is therefore to use service properties. This could look like:
#Component( property=HTTP_WHITEBOARD_FILTER_REGEX+"=/as")
public class DocumentServlet {
final Map<String,DocBuilder> builders = new ConcurrentHashMap<>();
public void doGet( HttpServletRequest rq, HttpServletResponse rsp )
throws IOException, ServletException {
InputStream in = getInputStream( rq.getPathInfo() );
if ( in == null )
....
String type = toType( rq.getPathInfo(), rq.getParameter("type") );
DocBuilder docbuilder = builders.get( type );
if ( docbuilder == null)
....
docbuilder.convert( type, in, rsp.getOutputStream() );
}
#Reference( cardinality=MULTIPLE, policy=DYNAMIC )
void addDocBuilder( DocBuilder db, Map<String,Object> props ) {
docbuilders.put(props.get("type"), db );
}
void removeDocBuilder(Map<String,Object> props ) {
docbuilders.remove(props.get("type"));
}
}
A DocBuilder could look like:
#Component( property = "type=ppt-pdf" )
public class PowerPointToPdf implements DocBuilder {
...
}

StructureMap Exception Code: 202

everyone, I have problems when using MVC3 code is as follows
public SystemController(IRepository repository)
:this
(
repository,
new AspNetMembershipProviderWrapper(System.Web.Security.Membership.Provider),
new AspNetMembershipProviderWrapper(System.Web.Security.Membership.Provider),
new AspNetRoleProviderWrapper(Roles.Provider),
new SmtpClientProxy(new SmtpClient(Utils.Setting.EmailServer,
int.Parse(Utils.Setting.EmailPort))
{
EnableSsl = true,
UseDefaultCredentials = true,
Credentials = new NetworkCredential(Utils.Setting.EmailAccount,
Utils.Setting.EmailPassword),
DeliveryMethod = SmtpDeliveryMethod.Network
})
){}
public SystemController(IRepository repository,
IUserService userService,
IPasswordService passwordService,
IRolesService rolesService,
ISmtpClient smtpClient)
: base(repository)
{
_userService = userService;
_passwordService = passwordService;
_rolesService = rolesService;
_smtpClient = smtpClient;
}
public class SmtpClientProxy : ISmtpClient
{
private readonly SmtpClient _smtpClient;
public SmtpClientProxy(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
#region ISmtpClient Members
public void Send(MailMessage mailMessage)
{
_smtpClient.Send(mailMessage);
}
#endregion
}
ObjectFactory.Initialize(x =>
{
x.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
});
x.For<ISessionFactory>()
.Singleton()
.Use(GetSessionFactory());
x.For<ISession>()
.HybridHttpOrThreadLocalScoped()
.Use(y => y.GetInstance<ISessionFactory>().OpenSession());
x.For<IUserService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IPasswordService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IPasswordService>()
.Use<AspNetMembershipProviderWrapper>();
x.For<IRolesService>()
.Use<AspNetRoleProviderWrapper>();
x.For<ISmtpClient>()
.Use<SmtpClientProxy>().Ctor<SmtpClient>();
x.For<MembershipProvider>()
.Use(System.Web.Security.Membership.Provider);
x.For<RoleProvider>()
.Use(Roles.Provider);
});
Error info:
StructureMap Exception Code: 202
No Default Instance defined for PluginFamily System.Net.Mail.SmtpClient, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
I think the problem lies in this:
x.For <ISmtpClient> ()
. Use <SmtpClientProxy> (). Ctor <SmtpClient> ();
I ask you how to write it?
Your SmtpClientProxy class requires an SmtpClient class in its constructor. You don't have anything registered for SmtpClient.
Try adding this to your registration:
x.For<SmtpClient>().Use<SmtpClient>();
This assumes that SmtpClient does not take dependencies in its constructor. If it does you will likely get an error that one of its dependencies are not registered with a default implementation.
Alternatively you could change the constructor code to this (no constructor dependency):
private readonly SmtpClient _smtpClient = new SmtpClient();
public SmtpClientProxy()
{
}
Without knowing what you are trying to do, it's hard to answer definitively.

Change ProfileProvider connectionstring on the fly

If you read my previous post, you know I'm able to use a custom MembershipProvider and RoleProvider to use different datasource on each call on the fly. I want to to the same thing with Profile.
My profile's propteries are not store in the web config but in a custom class like this :
public class AccountProfile : ProfileBase
{
public override SettingsProviderCollection Providers
{
get
{
return base.Providers;
}
}
static public AccountProfile GetUserProfile(string userName)
{
return (AccountProfile)(ProfileBase.Create(userName));
}
[SettingsAllowAnonymous(false)]
public string MobilePhone
{
get { return ((string)(base["MobilePhone"])); }
set { base["MobilePhone"] = value; Save(); }
}
}
also like for the Membership and RoleProvider I have a class like this :
public class MyProfileProvider : SqlProfileProvider
{
public MyProfileProvider()
{
}
public MyProfileProvider(string SubDomainInstanceName)
{
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
ProfileSection section = (ProfileSection)config.GetSection("system.web/profile");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
Initialize(section.DefaultProvider, membershipParams);
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
if (!string.IsNullOrEmpty(instance))
{
// Update the private connection string field in the base class.
string connectionString = "";//my connection
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
}
the difference betwen my CustomProfileProvider is that I can't use itself because the "create" method is in the ProfileBase. And with ILSpy I have seen a singleton and I wonder if it's not the source of the problem.
The issue is that I only pass one time in the initialize method. I can't do another time to change the datasource.
I hope you can understand my poor english and can help me.
I find a - bad - solution
In the CustomProfileBase class I add some code to change the connectionstring of the singleton instance of the class.
public class AccountProfile : ProfileBase
{
string connectionString = myconnstring;
//1st call not really use but allow to get an instance of my custom AccountProfile
AccountProfile test = (AccountProfile)(ProfileBase.Create(userName));
//change connectionstring oh the instance
FieldInfo connectionStringField = test.Providers["AspNetSqlProfileProvider"].GetType().BaseType.GetField("_sqlConnectionString", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
connectionStringField.SetValue(test.Providers["AspNetSqlProfileProvider"], connectionString);
//new call on the good datasource
return (AccountProfile)AccountProfile.Create(userName);
}
It's not the most beautifull solution by it's work.
What do you think of t ?

Resources