Accessing resources in Grails Plugin - grails

I have a Grails Plugin called 'foo' that uses another Grails Plugin called 'common'.
grails.plugin.location.'common' = "../common"
The 'common' plugin contains domain classes, as well as resource files (.properties files, xml templates, ...). These files are all located in subfolders in common/grails-app/conf/.
There's one class that implements NamespaceContext in my 'common' plugin that uses these files in order to function properly.
public class MyNamespaceContext implements NamespaceContext {
private Map<String, String> namespaces;
public MyNamespaceContext() {
final String XML_NAMESPACES_FILE = "grails-app/conf/xml/xmlNamespaces.properties";
try {
Properties xmlNamespaces = new Properties();
xmlNamespaces.load(new FileReader(XML_NAMESPACES_FILE));
namespaces = new HashMap<String, String>((Map) xmlNamespaces);
} catch (FileNotFoundException e) {
throw new RuntimeException("XML namespaces file '" + XML_NAMESPACES_FILE + "' cannot be found");
} catch (IOException e) {
throw new RuntimeException("IOException");
}
}
...
}
This class is used in several classes, also located in 'common' that form my domain model, implemented as xml decorators.
public class UserXmlDecorator implements User {
private Document xmlDocument;
private XPath xPath;
private final String rawXml;
public UserXmlDecorator(String rawXml) {
this.rawXml = rawXml;
this.xmlDocument = XmlDocumentFactory.INSTANCE.buildXmlDocumentInUTF8(rawXml);
this.xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
}
public String getUserName() {
try {
XPathExpression userNameXPathExpr = xPath.compile("...");
String userName = userNameXPathExpr.evaluate(appendixBXmlDocument);
return userName;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
public String getAge() {
try {
XPathExpression ageXPathExpr = xPath.compile("...");
String age = ageXPathExpr.evaluate(appendixBXmlDocument);
return age;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
When creating these decorators in my Grails Plugin 'foo', I get a FileNotFound exception, because it is looking for the template in foo/grails-app/conf/xml/xmlNamespaces.properties, instead of common/grails-app/conf/xml/xmlNamespaces.properties.
I've read
Grails: How to reference a resource located inside an installed plugin? but this could not help me.
Any idea how I can solve this?

Solved this by putting the .properties file in the classpath instead of the conf/ directory and then using the classloader to lod the resource.
xmlNamespaces.load(this.getClass().getClassLoader().getResourceAsStream(XML_NAMESPACES_FILE));

Related

Injecting HttpService into a Mule 4 Custom Configuration Properties Provider

I'm working on making a custom properties provider to load the contents of a Spring cloud config server at startup. I need to make a single call at the initialization of the provider to fetch these properties, and would like to use the Mule HttpService in order to make the http client for this call, instead of creating my own. Unfortunately, whenever I try this, it seems the HttpService hasn't been created yet and so throws an NPE once it's referenced.
CustomConfigurationPropertiesProviderFactory.java
public class CustomConfigurationPropertiesProviderFactory implements ConfigurationPropertiesProviderFactory {
public static final String EXTENSION_NAMESPACE = "custom-properties";
public static final String CONFIGURATION_PROPERTIES_ELEMENT = "config";
public static final ComponentIdentifier CUSTOM_CONFIGURATION_PROPERTIES =
builder().namespace(EXTENSION_NAMESPACE).name(CONFIGURATION_PROPERTIES_ELEMENT).build();
#Inject
HttpService httpService;
#Override
public ComponentIdentifier getSupportedComponentIdentifier() {
return CUSTOM_CONFIGURATION_PROPERTIES;
}
#Override
public ConfigurationPropertiesProvider createProvider(ConfigurationParameters parameters,
ResourceProvider externalResourceProvider) {
String url = parameters.getStringParameter("url");
return new CustomConfigurationPropertiesProvider(url, httpService);
}
}
CustomConfigurationPropertiesProvider.java
public class CustomConfigurationPropertiesProvider implements ConfigurationPropertiesProvider {
private final static String PREFIX = "custom::";
private Properties properties = null;
public CustomConfigurationPropertiesProvider(String url, HttpService httpService) {
HttpClientConfiguration.Builder builder = new HttpClientConfiguration.Builder();
builder.setName("customProperties");
HttpClient client = httpService.getClientFactory().create(builder.build()); //NPE here
client.start();
// proceed to create and execute request, then load into properties
}
#Override
public Optional<ConfigurationProperty> getConfigurationProperty(String configurationAttributeKey) {
if (configurationAttributeKey.startsWith(PREFIX)) {
String effectiveKey = configurationAttributeKey.substring(PREFIX.length());
if (properties != null && !properties.isEmpty()) {
return Optional.of(new ConfigurationProperty() {
#Override
public Object getSource() {...}
#Override
public Object getRawValue() { return properties.getProperty(effectiveKey); }
#Override
public String getKey() { return effectiveKey; }
});
}
}
return Optional.empty();
}
}
What do I need to change to properly inject this service?
I've been following the advice from these two bits of documentation, for reference:
https://docs.mulesoft.com/mule-runtime/4.2/custom-configuration-properties-provider
https://docs.mulesoft.com/mule-sdk/1.1/mule-service-injection

Sitecore Microsoft Dependency Injection error "Service has been disposed, cannot create object"

I am using Sitecore 8.2 Update 4 with Helix framework also using Microsoft Extension Dependency Injection. I have performed few steps for DI:
1. Created DI project in Foundation layer.
2. I have created on pipeline with name Habitat.Foundation.DI.RegisterControllers inside
App_config/Include/zFoundation
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"><sitecore> <services> <configurator type=" Habitat.Foundation.DI.RegisterControllers, Habitat.Foundation.DI" /></services></sitecore></configuration>
namespace Habitat.Foundation.DI{ public class RegisterControllers : IServicesConfigurator
{
public void Configure(IServiceCollection serviceCollection)
{
serviceCollection.AddMvcControllers("*.Feature.*");
}
}}
namespace Habitat.Foundation.DI.Extensions{
public static class ServiceCollectionExtensions
{
public static void AddMvcControllers(this IServiceCollection serviceCollection, params string[] assemblyFilters)
{
var assemblies = GetAssemblies.GetByFilter(assemblyFilters);
AddMvcControllers(serviceCollection, assemblies);
}
public static void AddMvcControllers(this IServiceCollection serviceCollection, params Assembly[] assemblies)
{
var controllers = GetTypes.GetTypesImplementing<IController>(assemblies)
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal));
foreach (var controller in controllers)
serviceCollection.AddTransient(controller);
controllers = GetTypes.GetTypesImplementing<ApiController>(assemblies)
.Where(controller => controller.Name.EndsWith("Controller", StringComparison.Ordinal));
foreach (var controller in controllers)
serviceCollection.AddTransient(controller);
}
}}
public static class GetAssemblies
{
public static Assembly[] GetByFilter(params string[] assemblyFilters)
{
var assemblyNames = new HashSet<string>(assemblyFilters.Where(filter => !filter.Contains('*')));
var wildcardNames = assemblyFilters.Where(filter => filter.Contains('*')).ToArray();
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(assembly =>
{
var nameToMatch = assembly.GetName().Name;
if (assemblyNames.Contains(nameToMatch)) return true;
return wildcardNames.Any(wildcard => IsWildcardMatch(nameToMatch, wildcard));
})
.ToArray();
return assemblies;
}
/// <summary>
/// Checks if a string matches a wildcard argument (using regex)
/// </summary>
private static bool IsWildcardMatch(string input, string wildcards)
{
return Regex.IsMatch(input, "^" + Regex.Escape(wildcards).Replace("\\*", ".*").Replace("\\?", ".") + "$",
RegexOptions.IgnoreCase);
}
}public static class GetTypes {
public static Type[] GetTypesImplementing<T>(params Assembly[] assemblies)
{
if (assemblies == null || assemblies.Length == 0)
return new Type[0];
var targetType = typeof(T);
return assemblies
.Where(assembly => !assembly.IsDynamic)
.SelectMany(GetExportedTypes)
.Where(type => !type.IsAbstract && !type.IsGenericTypeDefinition && targetType.IsAssignableFrom(type))
.ToArray();
}
private static IEnumerable<Type> GetExportedTypes(Assembly assembly)
{
try
{
return assembly.GetExportedTypes();
}
catch (NotSupportedException)
{
// A type load exception would typically happen on an Anonymously Hosted DynamicMethods
// Assembly and it would be safe to skip this exception.
return Type.EmptyTypes;
}
catch (ReflectionTypeLoadException ex)
{
// Return the types that could be loaded. Types can contain null values.
return ex.Types.Where(type => type != null);
}
catch (Exception ex)
{
// Throw a more descriptive message containing the name of the assembly.
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
"Unable to load types from assembly {0}. {1}", assembly.FullName, ex.Message), ex);
}
}
}
As I am using Glass Mapper so I want to use ISitecoreContext, for that I have register ISitecoreContext in Foundation/ORM for that I have performed these actions:
1. Created patch file inside Foundation/ORM
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"><sitecore><services>
<configurator type="Habitat.Foundation.ORM.DI.RegisterContainer, Habitat.Foundation.ORM" />
</services> </sitecore>
public class RegisterContainer : IServicesConfigurator
{
public void Configure(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<ISitecoreContext, SitecoreContext>();
}
}
Accesssing in controller like this
private readonly ISitecoreContext _sitecoreContext;
public TestController(ISitecoreContext sitecoreContext)
{
_sitecoreContext = sitecoreContext;
}
I accessing in two classes one way is _sitecoreContext.GetItem(Context.Site.ContentStartPath) and another way is _sitecoreContext.GetRootItem() then it started throwing error “{"Service has been disposed, cannot create object"}” in _sitecoreContext.GetItem(Context.Site.ContentStartPath) but working for
_sitecoreContext.GetRootItem()
When I update AddTransient to AddSingleton then it works for some time for both but for after some time it started giving me null value of _sitecoreContext.
Please help me.

spring-data-elastic Id field not populated on reads when using CustomEntityMapper

Elasticversion - 1.7.6
springboot - 1.3.5
Using spring-data-elasticsearch I have created a custom JSON mapping as advised elsewhere in order to support Java8 new datetime fields.
This works fine - but breaks reading entities from the repository as the id field no longer gets populated.
CustomConfig:
#Bean
#Autowired
public ElasticsearchTemplate elasticsearchTemplate(Client client) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return new ElasticsearchTemplate(client, new CustomEntityMapper(objectMapper));
}
public class CustomEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
#Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
#Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
Sample Entity :
#Document(indexName = "scanner", type = "Entry")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Entry {
#Id
private String id;
#Field(type= FieldType.String)
private String path;
#Field(type = FieldType.Date, format = DateFormat.date_time )
private OffsetDateTime created;
}
Note - that when I remove the CustomEntityMapper the id field is returned. I have traced the spring-data-elasticsearch code,
and identified that it fails to resolve the Id field from the elastic response in DefaultResultMapper.setPersistentId since
the mappingContext is null.
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
PersistentProperty<ElasticsearchPersistentProperty> idProperty = mappingContext.getPersistentEntity(clazz).getIdProperty();
// Only deal with String because ES generated Ids are strings !
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
Method setter = idProperty.getSetter();
if (setter != null) {
try {
setter.invoke(result, id);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
}
Has anyone experienced this issue? How can I support a CustomEntityMapper without breaking the Id resolution?
upgrading to spring boot 1.4.1-RELEASE resolved the issue

Web API, Light Inject and Passing a Static Dictionary to the data layer

We have a multi-database solution and are passing the connection string to a factory function like so:
container.Register<IDbContextFactory>(
f => new DynamicDbContextFactory(ClientConfig.GetConnectionString()),
new PerScopeLifetime());
ClientConfig contains a static dictionary that gets populated on app start that maps a sub domain to a connection string. It seems that this approach is causing a memory leak (not 100% sure about this causing the leak but there is a leak).
public class ClientConfig
{
private static ConcurrentDictionary<string, string> ConnectionStringManager
{
get;
set;
}
// etc.
}
My question is in MVC what is the best way to hold a list of connection strings that can be easily looked up on each request in order to pass that down the chain.
Edit : The question was initially tagged with Autofac
With Autofac you don't have to use a dictionary and something like that to do what you want. You can use a custom parameter :
public class ConnectionStringParameter : Parameter
{
public override Boolean CanSupplyValue(ParameterInfo pi,
IComponentContext context,
out Func<Object> valueProvider)
{
valueProvider = null;
if (pi.ParameterType == typeof(String)
&& String.Equals(pi.Name, "connectionString",
StringComparison.OrdinalIgnoreCase))
{
valueProvider = () =>
{
// get connectionstring based on HttpContext.Current.Request.Url.Host
return String.Empty;
};
}
return valueProvider != null;
}
}
Then register your Parameter using a Module
public class ConnectionStringModule : Autofac.Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing += registration_Preparing;
}
private void registration_Preparing(Object sender, PreparingEventArgs e)
{
Parameter[] parameters = new Parameter[] { new ConnectionStringParameter() };
e.Parameters = e.Parameters.Concat(parameters);
}
}
Module you have to register inside your container using
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
Each time Autofac have to resolve a parameter of type String named connectionString it will used the custom parameter and get your connectionstring based on what you want.
By the way this code sample use HttpContext.Current. In case of a multithreaded process it may return null. I don't recommend using HttpContext.Current for such things. You can use an intermediate class instead of accessing it, for example a IConnectionstringProvider interface.
public interface IConnectionstringProvider
{
String ConnectionString { get; }
}
public class ConnectionStringProvider : IConnectionstringProvider
{
public ConnectionStringProvider(Strong host)
{
// get connectionstring based on host
this._connectionString = String.Empty;
}
private readonly String _connectionString;
public String ConnectionString
{
get { return this._connectionString; }
}
}
Inside your Parameter you will have to change the valueProvider by
valueProvider = () =>
{
return context.Resolve<IConnectionstringProvider>().ConnectionString;
};
And finally you will have to register your IConnectionstringProvider at the beginning of the request lifetimescope :
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());
IContainer container = builder.Build();
container.ChildLifetimeScopeBeginning += container_ChildLifetimeScopeBeginning;
}
private static void container_ChildLifetimeScopeBeginning(
Object sender, LifetimeScopeBeginningEventArgs e)
{
String host = HttpContext.Current.Request.Url.Host;
ContainerBuilder childLifetimeScopeBuilder = new ContainerBuilder();
childLifetimeScopeBuilder.RegisterInstance(new ConnectionStringProvider(host))
.As<IConnectionstringProvider>()
.SingleInstance();
childLifetimeScopeBuilder.Update(e.LifetimeScope.ComponentRegistry);
}
}
Of course there is many way to do it but you have the idea

Grails service ->java.lang.NullPointerException: Cannot invoke method serviceMethod() on null object

I have Class in src/groovy . I want to use my service here . but error occurred "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here ". i try to debug but not able to find . can you please help me that what is my mistake .
class ListenerSession implements HttpSessionListener {
def transactionService = new TransactionService ()
public ListenerSession() {
}
public void sessionCreated(HttpSessionEvent sessionEvent){
}
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
User user=session["user"]
if(user){
try{
java.util.Date date = session['loginDate']
transactionService.updateUserLastLogin(user,date)
-----}catch (Exception e) {
println e
}
code in service is:
def updateUserLastLogin(User user,Date date){
try{
User.withTransaction{
println "121212"
user.lastLogin=date
user.loginDuration=new Date().time - user?.lastLogin?.time
def x=user.save()
}
}catch (Exception e) {
println e
}
}
Don't instantiate services with new. If they use nearly any piece of Grails framework, that piece won't work - like GORM session in this case.
Here's an exactly your question: http://grails.1312388.n4.nabble.com/Injecting-Grails-service-into-HttpSessionListener-can-it-be-done-td1379074.html
with Burt's answer:
ApplicationContext ctx = (ApplicationContext)ServletContextHolder.
getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
transactionService = (TransactionService) ctx.getBean("transactionService")
Grails won't inject your service for you in the src/groovy level and just declaring a new instance of TransactionService will not give you all the goodies (hence your error). You need to get your instance form the spring context like so...
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class ListenerSession implements HttpSessionListener {
public ListenerSession() {
}
public void sessionCreated(HttpSessionEvent sessionEvent){
}
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
User user=session["user"]
if(user){
try{
java.util.Date date = session['loginDate']
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def transactionService = ctx.transactionService
transactionService.updateUserLastLogin(user,date)
}catch (Exception e) {
println e
}
}
}
}

Resources