AEM servlet configuration factory missing after moving to AEM 6.3 - servlets

I forked the ACS commons for use in AEM 6.0 but in 6.3 the factory is missing from the /system/console/configMgr. I compared the code against the current ACS sitemap and the annotations are identical.
#Component(metatype = true,
label = "Apple SEO Site Map Servlet",
description = "SEO Site Map Servlet",
configurationFactory = true,
policy = ConfigurationPolicy.REQUIRE)
#Service
#SuppressWarnings("serial")
#Properties({#Property(name = "sling.servlet.resourceTypes", unbounded = PropertyUnbounded.ARRAY,
label = "Sling Resource Type", description = "Sling Resource Type for the Home Page component or components."),
#Property(name = "sling.servlet.selectors", value = "search-engine-sitemap", propertyPrivate = true),
#Property(name = "sling.servlet.extensions", value = "xml", propertyPrivate = true),
#Property(name = "sling.servlet.methods", value = "GET", propertyPrivate = true),
#Property(name = "webconsole.configurationFactory.nameHint",
value = "Site Map on resource types: [{sling.servlet.resourceTypes}]")})

Based on the discussion in the comments above, the issue was with setting up maven plugin to generate the scr descriptor and not the usage of annotations themselves.
Some additional information on how these annotations work :
The annotations are actually processed while the bundle is being created to generate xml files. It is the generated XML output that is used in the OSGI container. If you have a reason to believe that the annotations you have used are correct, then verify the xml that has been generated.
Before AEM 6.3 the general practice was to use felix scr annotations with maven-scr-plugin. Since 6.3 you can use annotations from the official OSGI package, this was added in R6 and hence the felix annotations were deprecated. You no longer need maven-scr-plugin and maven-bundle-plugin will process OSGI R6 annotations.
Since it is the xml that is used by the container, there's no reason for service declarations from <6.3 to stop working on 6.3. If there is an issue, it is typically in the build settings.
The scr description generated can be found at the following location of your workspace target/classes/OSGI-INF & target/classes/META-INF.
If you use OSGI R6 annotations and maven-bundle-plugin and don't see the generated xml files, add <exportScr>true</exportScr> to configuration section of the plugin config in POM. The POM generated by Adobe archetype skips this setting as of version 12

Related

BundleTransformer for less complaining "Could not find a factory, that creates an instance of the JavaScript engine"

If you upgrade from version 1 to version 2 of BundleTransformer you may get this message:
Could not find a factory, that creates an instance of the JavaScript
engine with name MsieJsEngine.
Like me, you may not even have realized you've upgraded more than just a point release.
How to fix?
Version 2 DOES NOT USE WEB.CONFIG for configuration anymore
So start by removing it and read the rest of this link
https://github.com/Taritsyn/JavaScriptEngineSwitcher/wiki/How-to-upgrade-applications-to-version-2.X
Basically you will be doing the following:
Removing existing web.config nodes for javscript engine
Adding to someplace like global.asax some initialization code
Install Nuget packages for the engines you want to use
Make sure to add a using statement to be able to use extension methods (if you choose that way)
I ended up with something like this:
using JavaScriptEngineSwitcher.Core;
using JavaScriptEngineSwitcher.Msie;
using JavaScriptEngineSwitcher.V8;
....
public class JsEngineSwitcherConfig
{
public static void Configure(JsEngineSwitcher engineSwitcher)
{
engineSwitcher.EngineFactories
.AddMsie(new MsieSettings
{
UseEcmaScript5Polyfill = true,
UseJson2Library = true
})
.AddV8();
engineSwitcher.DefaultEngineName = MsieJsEngine.EngineName;
}
}
I'm following the instructions, but my code is now breaking on BundleConfig
var cssTransformer = new StyleTransformer();
In the name attribute of /configuration/bundleTransformer/less/jsEngine configuration element not specified a name of JavaScript engine.
If you have not installed JavaScript engine, then for correct working
of this module is recommended to install one of the following NuGet
packages: * JavaScriptEngineSwitcher.Msie *
JavaScriptEngineSwitcher.V8 * JavaScriptEngineSwitcher.ChakraCore
After package is installed, need set a name of JavaScript engine (for
example, MsieJsEngine) to the name attribute of
/configuration/bundleTransformer/less/jsEngine configuration
element.

CQ5 SlingServlet and resourceTypes not working for specific resource paths

If I define a Sling Servlet as follows:
#SlingServlet(
label="TestResourceTypeServlet",
name = "com.company.project.servlets.TestResourceType",
extensions = {"bob"},
resourceTypes= {"cq:Page"},
methods= {"GET"},
metatype=true)
#Properties({
#Property(name = "service.description", value = "A test servlet"),
#Property(name = "service.vendor", value = "Company")
})
The servlet picks up any get request to every page with an extension of '.bob', which is fine but what I really want is to handle a request to a specific page type,
SO
I modify resourceTypes to read
resourceTypes= {"site-administration/components/page/page-distribution"},
the supplied value is the specific sling:resourceType (copied and pasted out of CRXDE Lite) of a page I am trying to access with the .bob extension, but I get a 404!!!
All the documentation I've read says the above should work, but it does not.
Out of desperation I've even tried "site-administration/components/page" which is the super type of the page I want.
I'm running a clean 5.6.1 instance with this servlet as part of an OSGi bundle.
Am I missing something obvious here, or if not is anyone aware of any hot fixes that could resolve this issue ?
Any help would be appreciated as I'm starting to go slightly mad in the head.
EDIT
Ok, so I've gotten a little further: If I access the page with:
[path-to-page]/page.bob.html
The servlet fires. But in this URL is bob not a selector? and if so why when the resource type is cq:Page does the configuration work with bob as an extension?
Very confused :-S
I'm obviously missing something very simple here.
The problem with pages is that the resourceType is stored on the jcr:content node below the cq:Page node. If you would call [path-to-page]/_jcr_content.bob it should work. Note: _jcr_content is an url save version of jcr:content.
Why your last example is actually working, I cannot tell.

Archetypes and Schemata() method

I am customizing Plone Archetypes content type by overriding Schemata() method to have vocabulary contents dynamically:
def Schemata(self):
""" Overrides field definitions in fly.
"""
# XXX: Cache this method?
schemata = getSchemata(self).copy()
settings = self.getResearchSettings()
for row in settings.getFieldCustomizations():
name = row.get("fieldName", None)
vocab = row.get("vocabToUse", None)
field = schemata.get(name, None)
if field and vocab:
# Modify field copy ion
field.vocabulery = vocab
return schemata
Do I need to use cache decorator for Schemata() function or is Archetypes handling Schemata() internally so smart so that it calls it only once per request?
Plone 4.0.
forgot Schemata... you should not touch that stuff.
you can have dynamic vocabularies using object methods or zope vocabularies.
first hit on google
You definitely want to cache the results of Schema() and Schemata(), as at least Schema() is called multiple times during the same request.

Setting the default page using quintagroup.transmogrifier generic setup import doesn't work with dexterity

We're using a quintagroup.transmogrifier content import profile to load content for our automated tests (very useful). Setting the default page doesn't seem to work.
The docs suggest quintagroup.transmogrifier supports setting default pages but not whether it does for generic set-up import steps. I eventually figured out you need to add a properties.xml file into the folder of the folderish item with the following:
<?xml version="1.0" encoding="utf-8"?>
<properties>
<property name="default_page" type="string">
index
</property>
</properties>
where index is replaced by the id of the default page and also in your import.cfg you need
[transmogrifier]
pipeline =
reader
…
propertiesimporter
[reader]
…
.properties.xml = propertymanager
[propertiesimporter]
blueprint = quintagroup.transmogrifier.propertiesimporter
However this doesn't work. We're running Plone 4.1rc3 + Dexterity 1.0 and presumably it's not compatible with Dexterity. I've tracked down the bit of code in quintagroup.transmogrifier.propertymanager.PropertiesImporterSection where it is falling down:
path = item[pathkey]
obj = self.context.unrestrictedTraverse(path, None)
Here path is a unicode string and unrestrictedTraverse returns None. If you use a byte string then it returns the correct object. Is this an incompatibility with Dexterity or am I doing something wrong?
This is a bug you'll need to report with the authors of the quintagroup.transmogrifier package. Paths must always be ASCII bytestrings, not Unicode objects. All sections in collective.transmogrifier (the underlying engine that quintagroup.transmogrifier uses) encode paths to ASCII.
Here is a code snippet from collective.transmogrifier.sections.constructor for example:
type_, path = item[typekey], item[pathkey]
fti = self.ttool.getTypeInfo(type_)
if fti is None: # not an existing type
yield item; continue
path = path.encode('ASCII')
elems = path.strip('/').rsplit('/', 1)
container, id = (len(elems) == 1 and ('', elems[0]) or elems)
context = self.context.unrestrictedTraverse(container, None)
Report it to the dedicated issue tracker on Plone.org so the authors can fix it for you.

Most Pythonic way to provide global configuration variables in config.py? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Improve this question
In my endless quest in over-complicating simple stuff, I am researching the most 'Pythonic' way to provide global configuration variables inside the typical 'config.py' found in Python egg packages.
The traditional way (aah, good ol' #define!) is as follows:
MYSQL_PORT = 3306
MYSQL_DATABASE = 'mydb'
MYSQL_DATABASE_TABLES = ['tb_users', 'tb_groups']
Therefore global variables are imported in one of the following ways:
from config import *
dbname = MYSQL_DATABASE
for table in MYSQL_DATABASE_TABLES:
print table
or:
import config
dbname = config.MYSQL_DATABASE
assert(isinstance(config.MYSQL_PORT, int))
It makes sense, but sometimes can be a little messy, especially when you're trying to remember the names of certain variables. Besides, providing a 'configuration' object, with variables as attributes, might be more flexible. So, taking a lead from bpython config.py file, I came up with:
class Struct(object):
def __init__(self, *args):
self.__header__ = str(args[0]) if args else None
def __repr__(self):
if self.__header__ is None:
return super(Struct, self).__repr__()
return self.__header__
def next(self):
""" Fake iteration functionality.
"""
raise StopIteration
def __iter__(self):
""" Fake iteration functionality.
We skip magic attribues and Structs, and return the rest.
"""
ks = self.__dict__.keys()
for k in ks:
if not k.startswith('__') and not isinstance(k, Struct):
yield getattr(self, k)
def __len__(self):
""" Don't count magic attributes or Structs.
"""
ks = self.__dict__.keys()
return len([k for k in ks if not k.startswith('__')\
and not isinstance(k, Struct)])
and a 'config.py' that imports the class and reads as follows:
from _config import Struct as Section
mysql = Section("MySQL specific configuration")
mysql.user = 'root'
mysql.pass = 'secret'
mysql.host = 'localhost'
mysql.port = 3306
mysql.database = 'mydb'
mysql.tables = Section("Tables for 'mydb'")
mysql.tables.users = 'tb_users'
mysql.tables.groups = 'tb_groups'
and is used in this way:
from sqlalchemy import MetaData, Table
import config as CONFIG
assert(isinstance(CONFIG.mysql.port, int))
mdata = MetaData(
"mysql://%s:%s#%s:%d/%s" % (
CONFIG.mysql.user,
CONFIG.mysql.pass,
CONFIG.mysql.host,
CONFIG.mysql.port,
CONFIG.mysql.database,
)
)
tables = []
for name in CONFIG.mysql.tables:
tables.append(Table(name, mdata, autoload=True))
Which seems a more readable, expressive and flexible way of storing and fetching global variables inside a package.
Lamest idea ever? What is the best practice for coping with these situations? What is your way of storing and fetching global names and variables inside your package?
How about just using the built-in types like this:
config = {
"mysql": {
"user": "root",
"pass": "secret",
"tables": {
"users": "tb_users"
}
# etc
}
}
You'd access the values as follows:
config["mysql"]["tables"]["users"]
If you are willing to sacrifice the potential to compute expressions inside your config tree, you could use YAML and end up with a more readable config file like this:
mysql:
- user: root
- pass: secret
- tables:
- users: tb_users
and use a library like PyYAML to conventiently parse and access the config file
I like this solution for small applications:
class App:
__conf = {
"username": "",
"password": "",
"MYSQL_PORT": 3306,
"MYSQL_DATABASE": 'mydb',
"MYSQL_DATABASE_TABLES": ['tb_users', 'tb_groups']
}
__setters = ["username", "password"]
#staticmethod
def config(name):
return App.__conf[name]
#staticmethod
def set(name, value):
if name in App.__setters:
App.__conf[name] = value
else:
raise NameError("Name not accepted in set() method")
And then usage is:
if __name__ == "__main__":
# from config import App
App.config("MYSQL_PORT") # return 3306
App.set("username", "hi") # set new username value
App.config("username") # return "hi"
App.set("MYSQL_PORT", "abc") # this raises NameError
.. you should like it because:
uses class variables (no object to pass around/ no singleton required),
uses encapsulated built-in types and looks like (is) a method call on App,
has control over individual config immutability, mutable globals are the worst kind of globals.
promotes conventional and well named access / readability in your source code
is a simple class but enforces structured access, an alternative is to use #property, but that requires more variable handling code per item and is object-based.
requires minimal changes to add new config items and set its mutability.
--Edit--:
For large applications, storing values in a YAML (i.e. properties) file and reading that in as immutable data is a better approach (i.e. blubb/ohaal's answer).
For small applications, this solution above is simpler.
How about using classes?
# config.py
class MYSQL:
PORT = 3306
DATABASE = 'mydb'
DATABASE_TABLES = ['tb_users', 'tb_groups']
# main.py
from config import MYSQL
print(MYSQL.PORT) # 3306
Let's be honest, we should probably consider using a Python Software Foundation maintained library:
https://docs.python.org/3/library/configparser.html
Config example: (ini format, but JSON available)
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
Code example:
>>> import configparser
>>> config = configparser.ConfigParser()
>>> config.read('example.ini')
>>> config['DEFAULT']['Compression']
'yes'
>>> config['DEFAULT'].getboolean('MyCompression', fallback=True) # get_or_else
Making it globally-accessible:
import configpaser
class App:
__conf = None
#staticmethod
def config():
if App.__conf is None: # Read only once, lazy.
App.__conf = configparser.ConfigParser()
App.__conf.read('example.ini')
return App.__conf
if __name__ == '__main__':
App.config()['DEFAULT']['MYSQL_PORT']
# or, better:
App.config().get(section='DEFAULT', option='MYSQL_PORT', fallback=3306)
....
Downsides:
Uncontrolled global mutable state.
A small variation on Husky's idea that I use. Make a file called 'globals' (or whatever you like) and then define multiple classes in it, as such:
#globals.py
class dbinfo : # for database globals
username = 'abcd'
password = 'xyz'
class runtime :
debug = False
output = 'stdio'
Then, if you have two code files c1.py and c2.py, both can have at the top
import globals as gl
Now all code can access and set values, as such:
gl.runtime.debug = False
print(gl.dbinfo.username)
People forget classes exist, even if no object is ever instantiated that is a member of that class. And variables in a class that aren't preceded by 'self.' are shared across all instances of the class, even if there are none. Once 'debug' is changed by any code, all other code sees the change.
By importing it as gl, you can have multiple such files and variables that lets you access and set values across code files, functions, etc., but with no danger of namespace collision.
This lacks some of the clever error checking of other approaches, but is simple and easy to follow.
Similar to blubb's answer. I suggest building them with lambda functions to reduce code. Like this:
User = lambda passwd, hair, name: {'password':passwd, 'hair':hair, 'name':name}
#Col Username Password Hair Color Real Name
config = {'st3v3' : User('password', 'blonde', 'Steve Booker'),
'blubb' : User('12345678', 'black', 'Bubb Ohaal'),
'suprM' : User('kryptonite', 'black', 'Clark Kent'),
#...
}
#...
config['st3v3']['password'] #> password
config['blubb']['hair'] #> black
This does smell like you may want to make a class, though.
Or, as MarkM noted, you could use namedtuple
from collections import namedtuple
#...
User = namedtuple('User', ['password', 'hair', 'name']}
#Col Username Password Hair Color Real Name
config = {'st3v3' : User('password', 'blonde', 'Steve Booker'),
'blubb' : User('12345678', 'black', 'Bubb Ohaal'),
'suprM' : User('kryptonite', 'black', 'Clark Kent'),
#...
}
#...
config['st3v3'].password #> passwd
config['blubb'].hair #> black
I did that once. Ultimately I found my simplified basicconfig.py adequate for my needs. You can pass in a namespace with other objects for it to reference if you need to. You can also pass in additional defaults from your code. It also maps attribute and mapping style syntax to the same configuration object.
please check out the IPython configuration system, implemented via traitlets for the type enforcement you are doing manually.
Cut and pasted here to comply with SO guidelines for not just dropping links as the content of links changes over time.
traitlets documentation
Here are the main requirements we wanted our configuration system to have:
Support for hierarchical configuration information.
Full integration with command line option parsers. Often, you want to read a configuration file, but then override some of the values with command line options. Our configuration system automates this process and allows each command line option to be linked to a particular attribute in the configuration hierarchy that it will override.
Configuration files that are themselves valid Python code. This accomplishes many things. First, it becomes possible to put logic in your configuration files that sets attributes based on your operating system, network setup, Python version, etc. Second, Python has a super simple syntax for accessing hierarchical data structures, namely regular attribute access (Foo.Bar.Bam.name). Third, using Python makes it easy for users to import configuration attributes from one configuration file to another.
Fourth, even though Python is dynamically typed, it does have types that can be checked at runtime. Thus, a 1 in a config file is the integer ‘1’, while a '1' is a string.
A fully automated method for getting the configuration information to the classes that need it at runtime. Writing code that walks a configuration hierarchy to extract a particular attribute is painful. When you have complex configuration information with hundreds of attributes, this makes you want to cry.
Type checking and validation that doesn’t require the entire configuration hierarchy to be specified statically before runtime. Python is a very dynamic language and you don’t always know everything that needs to be configured when a program starts.
To acheive this they basically define 3 object classes and their relations to each other:
1) Configuration - basically a ChainMap / basic dict with some enhancements for merging.
2) Configurable - base class to subclass all things you'd wish to configure.
3) Application - object that is instantiated to perform a specific application function, or your main application for single purpose software.
In their words:
Application: Application
An application is a process that does a specific job. The most obvious application is the ipython command line program. Each application reads one or more configuration files and a single set of command line options and then produces a master configuration object for the application. This configuration object is then passed to the configurable objects that the application creates. These configurable objects implement the actual logic of the application and know how to configure themselves given the configuration object.
Applications always have a log attribute that is a configured Logger. This allows centralized logging configuration per-application.
Configurable: Configurable
A configurable is a regular Python class that serves as a base class for all main classes in an application. The Configurable base class is lightweight and only does one things.
This Configurable is a subclass of HasTraits that knows how to configure itself. Class level traits with the metadata config=True become values that can be configured from the command line and configuration files.
Developers create Configurable subclasses that implement all of the logic in the application. Each of these subclasses has its own configuration information that controls how instances are created.

Resources