Attribute is required but not set (Unable to create snapshot class for interface) - configuration

My Liferay 7 server was using SomeModule happily, until I deployed a new version of SomeModule which has an additional required field favoriteColor.
Now whenever I try to load the portlet Liferay says:
java.lang.RuntimeException: Unable to create snapshot class for interface some.SomeModuleConfiguration
at com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil._createConfigurableSnapshot(ConfigurableUtil.java:77)
at com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil.createConfigurable(ConfigurableUtil.java:51)
at some.SomeModule.activate(SomeModule.java:50)
...
aused by: java.lang.IllegalStateException: Attribute is required but not set favoriteColor
at aQute.bnd.annotation.metatype.Configurable$ConfigurableHandler.invoke(Configurable.java:75)
at com.sun.proxy.$Proxy1220.favoriteColor(Unknown Source)
at some.SomeModuleConfigurationSnapshot407.<init>(Unknown Source)
The configuration UI for SomeModule does not show anything about favoriteColor.
How to fix that, for instance by setting favoriteColor to its default value?

An alternative path would be using a OSGi configuration file to set defaults and missing values. You can use those files as you do for those modules that come with liferay; e.g., elasticsearch config. (check your osgi/configs directory)

If you are lucky enough to have the source code of the module, you can solve this problem like this:
Temporarily make the new field optional, but replacing required = true to required = false in SomeModuleConfiguration.java.
Deploy the module.
Load the configuration page, save.
Restore to required = true.
Deploy again.
Alternative answers welcome!

Related

How can I externalize ISchedulerExecutorService to run tasks in an external hazelcast cluster(Hazecast 5.2) without using UserCodeDeployment?

I am working on externalizing our IScheduledExecutorService so I can run tasks externally on a external cluster. I am able to write a test and get the Runnable to actually run ONLY if I turn on UserCode deployment. If I want to change this task at all and run the tests again I get the below in my external cluster member's logs..
java.lang.IllegalStateException: Class com.mycompany.task.ScheduledTask is already in local cache and has conflicting byte code representation
I want to be able to change the task if I could and redeploy to Hazelcast to just handle it. I do this kind of thing with our external maps now. It can handle different versions of our objects using compact serialization.
Am I stuck using user code deployment for these functional objects? If I need to make a change to it I need to change the class name and redeploy to production. I'm hoping to get this task right the first time and not have to ever do that but I have a way of handling it if I do.
The cluster is already running in production and I'll have to add the following to each member
HZ_USERCODEDEPLOYMENT_ENABLED=true
and the appropriate client code(listed below) to enable this.
What I've done...
Added the following to my local docker file
HZ_USERCODEDEPLOYMENT_ENABLED=true
and also in the code that creates a hazelcast client connecting to my external cluster with
ClientConfig clientConfig = new ClientConfig(); ClientUserCodeDeploymentConfig clientUserCodeDeploymentConfig = new ClientUserCodeDeploymentConfig(); clientUserCodeDeploymentConfig.addClass("com.mycompany.task.ScheduledTask"); clientUserCodeDeploymentConfig.setEnabled(true); clientConfig.setUserCodeDeploymentConfig(clientUserCodeDeploymentConfig);
However, if I remove those two pieces I get the following Exception with a failing test. It doesn't know about my class at all.
com.hazelcast.nio.serialization.HazelcastSerializationException: java.lang.ClassNotFoundException: com.mycompany.task.ScheduledTask
Side Note:
We are using compact serialization for several maps already and when I try to configure this Runnable task via compact serialization I get the below error. I don't think that's the right approach either.
[Scheduler: myScheduledExecutorService][Partition: 121][Task: 7afe68d5-3185-475f-b375-5a82a7088de3] Exception occurred during run
java.lang.ClassCastException: class com.hazelcast.internal.serialization.impl.compact.DeserializedGenericRecord cannot be cast to class java.lang.Runnable (com.hazelcast.internal.serialization.impl.compact.DeserializedGenericRecord is in unnamed module of loader 'app'; java.lang.Runnable is in module java.base of loader 'bootstrap')
at com.hazelcast.scheduledexecutor.impl.ScheduledRunnableAdapter.call(ScheduledRunnableAdapter.java:49) ~[hazelcast-5.2.0.jar:5.2.0]
at com.hazelcast.scheduledexecutor.impl.TaskRunner.call(TaskRunner.java:78) ~[hazelcast-5.2.0.jar:5.2.0]
at com.hazelcast.internal.util.executor.CompletableFutureTask.run(CompletableFutureTask.java:64) ~[hazelcast-5.2.0.jar:5.2.0]

Can the ConfigurationAPI in Liferay DXP be used for Plugin sdk portlet?

I have followed given 2 tutorials to use COnfigurationAPI in a Liferay dxp plugins SDK portlet built using Ant/Ivy.
COnfiguration API 1
COnfiguration API 2.
Below is the configuration class used:
package com.preferences.interfaces;
import com.liferay.portal.configuration.metatype.annotations.ExtendedObjectClassDefinition;
import aQute.bnd.annotation.metatype.Meta;
#ExtendedObjectClassDefinition(
category = "preferences",
scope = ExtendedObjectClassDefinition.Scope.GROUP
)
#Meta.OCD(
id = "com.preferences.interfaces.UnsupportedBrowserGroupServiceConfiguration",
name = "UnsupportedBrowser.group.service.configuration.name"
)
public interface UnsupportedBrowserGroupServiceConfiguration {
#Meta.AD(deflt = "", required = false)
public String displayStyle();
#Meta.AD(deflt = "0", required = false)
public long displayStyleGroupId(long defaultDisplayStyleGroupId);
}
Post following the steps,I am getting the below error:
ERROR [CM Configuration Updater (ManagedService Update: pid=[com.preferences.interfaces.UnsupportedBrowserGroupServiceConfiguration])][org_apache_felix_configadmin:97] [org.osgi.service.cm.ManagedService, id=7082, bundle=297//com.liferay.portal.configuration.settings-2.0.15.jar?lpkgPath=C:\dev\Liferay\osgi\marketplace\Liferay Foundation.lpkg]: Unexpected problem updating configuration com.preferences.interfaces.UnsupportedBrowserGroupServiceConfiguration {org.osgi.service.cm.ConfigurationAdmin}={service.vendor=Apache Software Foundation, service.pid=org.apache.felix.cm.ConfigurationAdmin, service.description=Configuration Admin Service Specification 1.2 Implementation, service.id=56, service.bundleid=643, service.scope=bundle}
Caused by: java.lang.IllegalArgumentException: wrong number of arguments
So,does this process need a osgi module as mandatory or can we do it using plusings sdk portlet built using ant as well?
Without disecting the error message Caused by: java.lang.IllegalArgumentException: wrong number of arguments:
The way you build your plugin (Ant, Maven, Gradle, manually) doesn't make a difference, as long as you build a plugin that will be understood by the runtime. aQute.bnd.annotation.metatype.Meta points firmly into the OSGi world, and makes it almost certain that you'll need an OSGi module. You can build this with Ant, of course. Even in Ant you can embed tools like bnd, or you can write the proper Manifest.mf to include in your module manually (just kidding - you don't want to do it manually, but it would work).
Recommendation: Instead of moving everything over: Try to reproduce this with a minimal example in gradle or better Liferay Workspace (which is gradle based), just to get all the automatic wiring in. Check if it makes a difference and compare the generated output from your Ant build process with the workspace output. Pay specific attention to the Manifest.
In order to build the proper Manifest, you want to use bnd - if the Manifest turns out to be your issue: Find a way to embrace bnd - if that's by saying goodby to Ant, or by tweaking your build script remains your decision.

Configuration management in builds without source controlled config transformations

Given a configuration named "Data:ConnectionString" in appsettings.json file (ASP.NET Core application), how do I override this in the build? By overriding it can either be that there is a step which changes the value in appsettings.json before compilation during build, or that I override the parameter when using "dotnet test", or something else.
More info:
I have a ASP.NET Core application with standard configuration in appsettings.json. I do not want any connection string or sensitive data checked in the source control.
I am building my application using Visual Studio Team Service (cloud TFS). There is a step where tests are executed, and I want these tests to run against a remote service for which I do not want to check in the credentials.
There are a number of extensions available on http://marketplace.visualstudio.com that will help you without any complicated ness.
https://marketplace.visualstudio.com/items?itemName=YodLabs.VariableTasks
I like the Variable Tasks Pack that comes with:
Set Variable Set a variable value and optionally apply a transformation to it.
Set Variables with Credential Sets the username/password from an existing service endpoint
Set Variable from JSON Extracts a value from JSON using JSONPath
Set Variable from XML Extracts a value from XML using XPath
Update Build Number Allows you to change a build number
Increment Version Increments a semver version number
Super easy... You can also just search for "json" or "variable" to find other options...
Most popular ways:
Use app secrets
Use scripts section in your project.json. You have 4 events -
precompile, postcompile, prepublish, postpublish
You can set the an environmental variable ASPNETCORE_ENVIRONMENT in the build to something like "Test". Create an appsettings.json file named appsettings.Test.Json. Then when you are setting up your configuration in Startup.cs do something like...
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
When the environmental variable is set to TEST, you new appsettings file will be loaded and can set the connection string to whatever you want.

Bean missing error when deploying SnappyData-0.5 pulse.war

I am trying to deploy the Pulse Web Application to an external Tomcat. I get this error when deploying. How should I fix this?
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'org.springframework.security.authenticationManager' is
defined: Did you forget to add a gobal
element to your configuration (with child
elements)? Alternatively you can use the authentication-manager-ref
attribute on your and elements.
OK. This is fixed. To everyone also experiencing this... you must set the Spring Profile "pulse.authentication.default" or it will not load the AuthenticationManager Bean.
The overall issue is with the RowStore's documentation, which says this is OPTIONAL, when in fact it is required.
http://rowstore.docs.snappydata.io/docs/manage_guide/pulse/quickstart.html#topic_795C97B46B9843528961A094EE520782
It says at Step 4.) that configuring security is Optional when in fact you have to pass a Spring Profile. Also, again in the section "Authenticating Pulse Users", it says this is not a requirement.
To fix the issue I had to pass the Spring Profile "pulse.authentication.default" to activate the Bean in spring-security.xml and deploy pulse.war properly.
A better way for SnappyData pulse.war to do this in the future might be to use "!pulse.authentication.custom", which would always load the default AuthenticationManager bean as long as a custom one was not configured.
Example change for future to make it truly optional:
<beans:beans profile="!pulse.authentication.custom" >
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Which version of Tomcat are you using?
Here is another thread on the same issue with TC authentication.
Else, can you just try Pulse in the "embedded mode" ?
Which version of SnappyData you are using ?
You need to mention a pulse.properties file in the classpath . For details you can check http://rowstore.docs.snappydata.io/docs/manage_guide/pulse/quickstart.html#topic_795C97B46B9843528961A094EE520782.
Let us know if you any problems further.

Why does DriverManager.getConnection() lookup fail in GroovyConsole?

The following Groovy script works correctly from the command line. (I successfully get a Connection.)
// ---- jdbc_test.groovy
import java.sql.*
Class.forName("com.mysql.jdbc.Driver")
def con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test",
"root",
"password")
println con
> groovy -cp lib\mysql-connector-java-5.1.25-bin.jar script\jdbc_test.groovy
com.mysql.jdbc.JDBC4Connection#6025e1b6
But if the same script is loaded into GroovyConsole (2.4.3) and run - after adding the mysql-connector-java-5.1.25-bin.jar using 'Script' | 'Add Jar(s) to ClassPath' - it fails:
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/test
at java_sql_DriverManager$getConnection.call(Unknown Source)
at jdbc_test.run(jdbc_test.groovy:3)
Every other package or class I have added to the classpath in GroovyConsole and experimented with has worked. Is there some unexpected interaction of Groovy's class loading and the way DriverManager works?
Is there a way around this? I'm trying to use the GroovyConsole to interactively test out some JDBC code (a library of functions, each of which takes a Connection as its first argument).
UPDATE: The Class.forName() part appears to be working fine. If I 'Script' | 'Clear Script Context' and rerun the script in GroovyConsole, I instead get:
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
If I add mysql-connector-java-5.1.25-bin.jar back in, I go back to getting:
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/test
Solution: The driver has to be on the classpath.
Reason:
If you look into the DriverManager class you find code like this: Class.forName(driver.getClass().getName(), true, classLoader);. This is to check if the driver is accessible from your classloader context. And that context is determined by going back to the class that is calling he DriverManager. This code is written for Java, thus assumes a certain amount of frames on the call stack to go back. Since Groovy does not do direct calls (unless you use #CompileStatic) this number is wrong and leads usually to a Groovy core class to be selected, resulting in Groovy main loader to be selected.... in the past this was often even the JDK system classloader because of reflection. So even though the driver is loaded and registered, it is not accessible to you.
Note: with jdbc4 the driver should register itself, just by being on the classpath