Sitecore Pipeline (indexing.filterIndex.inbound) not being called - lucene

I am trying to create a Lucene index in Sitecore 8.x of items that are visible to unauthenticated users (extranet\Anonymous). In order to do this I am trying to use the indexing.filterIndex.inbound pipeline.
I have tried writing a custom pipeline that returns false if the item cannot be read as extranet\Anonymous:
public class ApplyInboundIndexAccessFilter : InboundIndexFilterProcessor
{
public override void Process(InboundIndexFilterArgs args)
{
var item = args.IndexableToIndex as SitecoreIndexableItem;
var anonymousUser = Sitecore.Security.Accounts.User.FromName("extranet\\anonymous", false);
if (!item.Item.Security.CanRead(anonymousUser))
{
args.IsExcluded = true;
}
}
}
but at no time does this pipeline get invoked.
I have added my config (tried it with the default, before, after, with the default removed)
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<indexing.filterIndex.inbound>
<processor type="MyApplication.Site.Features.ContentSearch.IndexFilters.ApplyInboundIndexAccessFilter, MyApplication.Site">
<includedIndexNames hint="list">
<indexName>siteSearchIndex_web</indexName>
</includedIndexNames>
<excludedIndexNames hint="list">
<indexName>siteSearchIndex_master</indexName>
</excludedIndexNames>
</processor>
</indexing.filterIndex.inbound>
</pipelines>
</sitecore>
</configuration>
Am I right in assuming that this should be called on indexing, if not, when?
Any suggestions would be gratefully received.

Related

Bean Validation with JAX-RS (rest-easy): parameter name not recognized

I'm using JAX-RS resources with Bean Validation and integration between these two works as expected.
However, the default error messages generated in case of a validation error report parameter names as arg0, like so
[PARAMETER]
[login.arg0.password]
[password is required]
[]
Corresponding method definition:
#POST //and other JAX-RS annotations
public Response login(
#NotNull
#Valid
LoginBody loginBody) {
[...]
protected static class LoginBody {
#NotNull(message = EMAIL_REQUIRED)
public String email;
#NotNull(message = PASSWORD_REQUIRED)
public String password;
}
While I'm generally fine with this message pattern, what actually is annyoing, is the fact that the original parameter name is not recognized, i. e. I'd rather like to see
login.loginBody.password instead of arg0.
Is there an easy way to fix this, e. g. somehow provide an explicit name for that parameter?
I'm using WildFly Swarm 2017.6.0. From what I found out this means I have resteasy + resteasy-validator + hibernate-validator
Thanks.
You could try to compile your app with -parameters or instruct your IDE to do so, e.g. in case of
eclipse: preferences -> java -> compiler -> "store information about method parameters (usable via reflection)"
With that in place you then need to instruct the Bean Validation infrastructure (e.g. ) hibernate-validator to
use the ReflectiveParameterNamer via META-INF/validation.xml.
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider</parameter-name-provider>
See also Hibernate Validator Configuration
I got something reliably working with the Paranamer library
META-INF/validation.xml:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/xml/ns/javax/validation/configuration
validation-configuration-1.1.xsd"
version="1.1">
<default-provider>org.hibernate.validator.HibernateValidator
</default-provider>
<message-interpolator>org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
</message-interpolator>
<traversable-resolver>org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver
</traversable-resolver>
<constraint-validator-factory>org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl
</constraint-validator-factory>
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
To get paranamer working with wildfly I needed to create a parameter-namer jboss-module
and reference that module from the module.xml of the hibernate-validator module.
With that in place I could simply write:
#POST
public Response login(#NotNull #Valid #Named("authRequest") AuthRequest authRequest) {
return Response.ok().build();
}
...
public class AuthRequest {
#NotNull(message = AuthMessages.EMAIL_REQUIRED)
public String email;
#NotNull(message = AuthMessages.PASSWORD_REQUIRED)
public String password;
}
which yields the following response for a request sent via curl:
curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"email":"foo#bar.com"}' -v http://localhost:8080/javaweb-training/resources/auth
Response:
{"exception":null,"fieldViolations":[],"propertyViolations":[],"classViolations":[],"parameterViolations":[{"constraintType":"PARAMETER","path":"login.authRequest.password","message":"password.required","value":""}],"returnValueViolations":[]}%
... note login.authRequest.password instead of just login.arg0.password
There is a very simple solution: you can set your own error message in the constraint definition as follows
#NotNull(message = "password is required")
If you want a more generic solution based on the JAX-RS parameter annotations you can implement your own simple ParameterNamProvider and register it in validation.xml as follows. This has the advantage of not having to change the jboss module structure. I also didn't have to change any compiler flags...
public class AnnotatedParameterNameProvider implements ParameterNameProvider {
#Override
public List<String> getParameterNames(Constructor<?> constructor) {
return lookupParameterNames(constructor.getParameterAnnotations());
}
#Override
public List<String> getParameterNames(Method method) {
return lookupParameterNames(method.getParameterAnnotations());
}
private List<String> lookupParameterNames(Annotation[][] annotations) {
final List<String> names = new ArrayList<>();
if (annotations != null) {
for (Annotation[] annotation : annotations) {
String annotationValue = null;
for (Annotation ann : annotation) {
annotationValue = getAnnotationValue(ann);
if (annotationValue != null) {
break;
}
}
// if no matching annotation, must be the request body
if (annotationValue == null) {
annotationValue = "requestBody";
}
names.add(annotationValue);
}
}
return names;
}
private static String getAnnotationValue(Annotation annotation) {
if (annotation instanceof HeaderParam) {
return ((HeaderParam) annotation).value();
} else if (annotation instanceof PathParam) {
return ((PathParam) annotation).value();
} else if (annotation instanceof QueryParam) {
return ((QueryParam) annotation).value();
}
return null;
}
}
In validation.xml:
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd"
version="1.1">
<parameter-name-provider>com.yourcompany.providers.AnnotatedParameterNameProvider</parameter-name-provider>
</validation-config>
Note that you can also customize how the error message is formatted by implementing your own MessageInterpolator and registering it in the validation.xml
Can you try to implement an exception mapper for ConstraintViolationExceptions and see if the information you have there (the list of constraint violations) can help you to obtain the parameter name?
Updated version of #thomas-darimont for Hibernate Validator 6.X.
Variant#1 - with build in Java 8 (using -parameters compile parameter)
Specify dependencies (gradle example):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
Specify validator(s):
#GET
#Path("user/{userId}")
public Response getUser(#Size(min = 2) #PathParam("userId") String userId) {
return null;
}
Note: org.hibernate.validator.internal.engine.DefaultParameterNameProvider will return parameter names obtained from the Java reflection API.
Variant #2 - use ParaNamer library. (xml configuration)
In case you don't want to be dependant on compilation flag.
Specify dependencies (gradle example):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
// ParaNamer library
implementation('com.thoughtworks.paranamer:paranamer:2.8')
Specify validator(s):
#GET
#Path("user/{userId}")
public Response getUser(#Size(min = 2) #PathParam("userId") String userId) {
return null;
}
Put <project_dir>/src/main/resources/META-INF/validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration
http://xmlns.jcp.org/xml/ns/validation/configuration/validation-configuration-2.0.xsd"
version="2.0">
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
Note: Since Hibernate Validator 6.x org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider is deprecated, use org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider instead.
Question: Can I configure this with Java-code style only?
Unfortunately, no. (See details here).

Best practices for prepopulated tables via OrmLite in Servicestack

I'm generating tables via OrmLite and I was wondering about best practices for prepopulating tables. Example tables - countries, states, cities, etc.
I can think of a few ways to pre-populate tables:
List item
Seed DB
API (when possible)
Static file
In code
Separate project
However, in some cases the data could get large as in the example of cities around the world so in code is not viable.
I could also consider generating tables that need to be pre-populated directly via another project where I can fetch data from a source and get it into the DB.
However, I was wondering about the scenario when you do generate it via an ORM (especially in production). How would you approach the problem?
This must be a common problem across all ORM's.
If it's only code tables like countries, states, etc, they're small enough to still have them as part of the project, normally I'd create a separate static class called SeedData with all the data in POCO's
1. Maintaining Code Tables in Host Project
public static class SeedData
{
public static List<Country> Countries
{
get { return new[] { new Country(...), ... }; }
}
}
Then in your AppHost populate add a flag on whether to re-create them on startup, e.g:
public void Configure(Container container)
{
var appSettings = new AppSettings(); //Read from Web.config <appSettings/>
if (appSettings.Get("RecreateTables", false))
{
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
Change AppSetting to recreate tables
This will then let you re-create the tables and re-populate the data when you change the RecreateTables appSetting to True, e.g:
<appSettings>
<add key="RecreateTables" value="True" />
</appSettings>
As the default behavior of ASP.NET will automatically restart the AppDomain, just saving a change to Web.config is enough to restart your ASP.NET application the next time any page gets refreshed.
2. Add to Test Project in adhoc Explicit Test
If the Data gets too big to fit in the working project I would first move it to a separate test project inside an [Explicit] text fixture (so it's never automatically run), that you can easily run manuallu, e.g:
[Explicit]
[TestFixture]
public class AdminTasks
{
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
db.InsertAll(SeedData.Countries);
...
}
}
}
3. Save data in external static text Files
Finally if the data is even too big to fit in C# classes, I would then save it out to a static file in the test that you can easily re-hydrate into POCO's that you can populate with OrmLite, e.g:
[Test]
public void Recreate_and_populate_tables()
{
var dbFactory = new OrmLiteConnectionFactory(...);
using (var db = dbFactory.Open())
{
db.DropAndCreateTable<Country>();
var countries = File.ReadAllText("~/countries.txt".MapAbsolutePath())
.FromJson<List<Country>>();
db.InsertAll(countries);
...
}
}

AutoStart/Pre-warm features not working in IIS 7.5 / WCF service

For testing the many headaches of IIS/WCF implementation from scratch, I built the HelloWorld service and client walked through (very nicely) here. I added endpoints for net.tcp, and the service is working properly end-to-end for both bindings under IIS 7.5 (on Windows 7) in its own ApplicationPool called HW.
What I'm trying to get working is the announced AutoStart and Preload (or "pre-warm caching") features. I've followed the instructions laid out here and here (quite similar to one another, but always good to have a second opinion) very closely. Which means I
1) Set the application pool startMode...
<applicationPools>
<!-- ... -->
<add name="HW" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" />
</applicationPools>
2) ...enabled serviceAutoStart and set a pointer to my serviceAutoStartProvider
<site name="HW" id="2">
<application path="/" applicationPool="HW" serviceAutoStartEnabled="true" serviceAutoStartProvider="PreWarmMyCache" />
<!-- ... -->
</site>
3) ...and named said provider, with the GetType().AssemblyQualifiedName of the class listed in its entirety below
<serviceAutoStartProviders>
<add name="PreWarmMyCache" type="MyWCFServices.Preloader, HelloWorldServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</serviceAutoStartProviders>
using System;
namespace MyWCFServices
{
public class Preloader : System.Web.Hosting.IProcessHostPreloadClient
{
public void Preload(string[] parameters)
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(#"C:\temp\PreloadTest.txt");
sw.WriteLine("Preload executed {0:G}", DateTime.Now);
sw.Close();
}
}
}
Alas, all this manual configuration, plus a couple iisreset calls, and I get nothing. No w3wp.exe process firing up in Task Manager (though I get it if I launch the HelloWorldClient), no text file, and above all, no satisfaction.
There is a frustratingly scant amount of discussion about this feature, either on SO or the wider web, and the few similar questions here got little attention, all of which rings an alarm bell or two. Perhaps needlessly though--any experts out there who have been down this very road a time or two care to chime in? (Happy to offer up the entire solution if you can suggest a good place to host it.)
EDIT: I tried resetting that path in the Preload method to the relative App_Data folder (another SO answer suggested that), didn't matter. Also, I learned the w3wp.exe process fires on a simple browse to the localhost. The process consumes an impressive 17MB of memory to serve up its single tiny OperationContract, while for the price offering zero Preload value. 17MB of ColdDeadCache.
This is a slightly different approach for your problem:
Use Windows Server AppFabric for service auto-start
Use WCF infrastructure to execute custom startup code
Re 1: The Appfabric AutoStart feature should just work out of the box (provided you're not using MVC's ServiceRoute to register your services, they MUST be specified either in the Web.config's serviceActivations section or using physical *.svc files.
Re 2: To inject custom startup code into the WCF pipeline you could use an attribute like this:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace WCF.Extensions
{
/// <summary>
/// Allows to specify a static activation method to be called one the ServiceHost for this service has been opened.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class ServiceActivatorAttribute : Attribute, IServiceBehavior
{
/// <summary>
/// Initializes a new instance of the ServiceActivatorAttribute class.
/// </summary>
public ServiceActivatorAttribute(Type activatorType, string methodToCall)
{
if (activatorType == null) throw new ArgumentNullException("activatorType");
if (String.IsNullOrEmpty(methodToCall)) throw new ArgumentNullException("methodToCall");
ActivatorType = activatorType;
MethodToCall = methodToCall;
}
/// <summary>
/// The class containing the activation method.
/// </summary>
public Type ActivatorType { get; private set; }
/// <summary>
/// The name of the activation method. Must be 'public static void' and with no parameters.
/// </summary>
public string MethodToCall { get; private set; }
private System.Reflection.MethodInfo activationMethod;
#region IServiceBehavior
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
serviceHostBase.Opened += (sender, e) =>
{
this.activationMethod.Invoke(null, null);
};
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
// Validation: can get method
var method = ActivatorType.GetMethod(name: MethodToCall,
bindingAttr: System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,
callConvention: System.Reflection.CallingConventions.Standard,
types: Type.EmptyTypes,
binder: null,
modifiers: null);
if (method == null)
throw new ServiceActivationException("The specified activation method does not exist or does not have a valid signature (must be public static).");
this.activationMethod = method;
}
#endregion
}
}
..which can be used like this:
public static class ServiceActivation
{
public static void OnServiceActivated()
{
// Your startup code here
}
}
[ServiceActivator(typeof(ServiceActivation), "OnServiceActivated")]
public class YourService : IYourServiceContract
{
}
That's the exact approach we've been using for quite a while and on a large number of services. The extra benefit of using a WCF ServiceBehavior for custom startup code (as opposed to relying on the IIS infrastructure) is that it works in any hosting environment (incl. self-hosted) and can be more easily tested.
I know this sounds absurd but I faced the same issue (w3wp.exe not firing automatically after making the config changes) and it was because I hadn't run the text editor in Admin mode when I was editing the applicationHost.config file. Stupid mistake on my part.
In my defense I was using Notepad++ which told me it was saving when it actually wasn't.
I've done the same. it works...
In preload method I have some code copied from a nice white paper available here!
Preload method looks like...
public void Preload(string[] parameters)
{
bool isServceActivated = false;
int attempts = 0;
while (!isServceActivated && (attempts <10))
{
Thread.Sleep(1 * 1000);
try
{
string virtualPath = "/Test1/Service1.svc";
ServiceHostingEnvironment.EnsureServiceAvailable(virtualPath);
isServceActivated = true;
}
catch (Exception exception)
{
attempts++;
//continue on these exceptions, otherwise fail fast
if (exception is EndpointNotFoundException ||
exception is ServiceActivationException ||
exception is ArgumentException)
{
//log
}
else
{
throw;
}
}
}
}
Maybe you are on a 64-bit system? There is a known "feature" in Windows where the save gets redirected to the 32 bit folder and thus no changes will be picked up
(I have converted my comment to an answer as answers might be easier to find)

RavenDB - One client can't see changes from a different client

I'm running two instances of my application. In one instance, I save one of my entities. When I check the RavenDB (http://localhost:8080/raven), I can see the change. Then, in my other client, I do this (below), but I don't see the changes from the other application. What do I need to do in order to get the most recent data in the DB?
public IEnumerable<CustomVariableGroup> GetAll()
{
return Session
.Query<CustomVariableGroup>()
.Customize(x => x.WaitForNonStaleResults());
}
Edit: The code above works if I try to make a change and get a concurrency exception. After that, when I call refresh (which invokes the above code), it works.
Here is the code that does the save:
public void Save<T>(T objectToSave)
{
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
Session.Store(objectToSave, eTag);
Session.SaveChanges();
}
And here is the class that contains the Database and Session:
public abstract class DataAccessLayerBase
{
/// <summary>
/// Gets the database.
/// </summary>
protected static DocumentStore Database { get; private set; }
/// <summary>
/// Gets the session.
/// </summary>
protected static IDocumentSession Session { get; private set; }
static DataAccessLayerBase()
{
if (Database != null) { return; }
Database = GetDatabase();
Session = GetSession();
}
private static DocumentStore GetDatabase()
{
string databaseUrl = ConfigurationManager.AppSettings["databaseUrl"];
DocumentStore documentStore = new DocumentStore();
try
{
//documentStore.ConnectionStringName = "RavenDb"; // See app.config for why this is commented.
documentStore.Url = databaseUrl;
documentStore.Initialize();
}
catch
{
documentStore.Dispose();
throw;
}
return documentStore;
}
private static IDocumentSession GetSession()
{
IDocumentSession session = Database.OpenSession();
session.Advanced.UseOptimisticConcurrency = true;
return session;
}
}
Lacking more detailed information and some code, I can only guess...
Please make sure that you call .SaveChanges() on your session. Without explicitly specifiying an ITransaction your IDocumentSession will be isolated and transactional between it's opening and the call to .SaveChanges. Either all operations succeed or none. But if you don't call it all your previous .Store calls will be lost.
If I was wrong, please post more details about your code.
EDIT: Second answer (after additional information):
Your problem has to do with the way RavenDB caches on the client-side. RavenDB by default caches every GET request throughout a DocumentSession. Plain queries are just GET queries (and no, it has nothing to do wheter your index in dynamic or manually defined upfront) and therefore they will be cached. The solution in your application is to dispose the session and open a new one.
I suggest you rethink your Session lifecycle. It seems that your sessions live too long, otherwise this concurrency wouldn't be an issue. If you're building a web-application I recommend to open and close the session with the beginning and the end of your request. Have a look at RaccoonBlog to see it implemented elegantly.
Bob,
It looks like you have but a single session in the application, which isn't right. The following article talks about NHibernate, but the session management parts applies to RavenDB as well:
http://archive.msdn.microsoft.com/mag200912NHibernate
This code is meaningless:
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
Session.Store(objectToSave, eTag);
It basically a no op, but one that looks important. You seems to be trying to work with a model where you have to manually manage all the saves, don't do that. You only need to manage things yourself when you create a new item, that is all.
As for the reason you get this problem, here is a sample:
var session = documentStore.OpenSession();
var post1 = session.Load<Post>(1);
// change the post by another client
post2 = session.Load<Post>(1); // will NOT go to the server, will give the same instance as post1
Assert.ReferenceEquals(post1,post2);
Sessions are short lived, and typically used in the scope of a single form / request.

ejb3.1 #Startup.. #Singleton .. #PostConstruct read from XML the Objects

I need to initialize a set of static String values stored in an XML files [ I know this is against the EJB spec ]
as shown below since the over all Idea is to not hardcore within EJB's the JNDI info
Utils.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="jndidb">java:jdbc/MYSQLDB10</entry>
<entry key="jndimdbque">java:jms/QueueName/remote</entry>
<entry key="jndi1">DBConnections/remote</entry>
<entry key="jndi2">AddressBean/remote</entry>
</properties>
The Onload of ejbserver startup code is as follows ...
inpstrem = clds.getClassLoaders(flename) Reads the Util.xml and stores the same in Hashtable key value pare....
package com.ejb.utils;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.Singleton;
import javax.ejb.Startup;
#Singleton
#Startup
public class StartupUtils {
private final String INITFILENAME = "/System/Config/Utils.xml";
private static Hashtable HTINITFLENME=null,HTERRINITFLENME=null,HTCMMNFLENME=null;
public StartupUtils() {
HTINITFLENME = new Hashtable();
HTERRINITFLENME = new Hashtable();
}
public void printAll(Hashtable htcmmnflenme){
Enumeration ENUMK = null, VALS = null;
String KEY = "", VALUE = "";
ENUMK = htcmmnflenme.keys();
while (ENUMK.hasMoreElements()) {
KEY = null;VALUE = null;
KEY = (ENUMK.nextElement().toString().trim());
VALUE = htcmmnflenme.get(KEY).toString().trim();
InitLogDisplay(KEY + " :::: " + VALUE);
}
}
public static void InitLogDisplay(String Datadisplay){
System.out.println(Datadisplay);
}
public Hashtable getDataProp(String flename){
Map htData = null;
InputStream inpstrem = null;
ClassLoaders clds = null;
Enumeration enumk = null, vals = null;
String key = "", value = "";
Properties props = null;
Hashtable htx = null;
try {
clds = new ClassLoaders();
inpstrem = clds.getClassLoaders(flename);
props = new Properties();
props.loadFromXML(inpstrem);
enumk = props.keys();
vals = props.elements();
htData = new HashMap();
htData = new TreeMap();
while (enumk.hasMoreElements()) {
key = (enumk.nextElement().toString().trim());
value = (vals.nextElement().toString().trim());
htData.put(key,value);
}
clds = null;
props = null;
inpstrem.close();
} catch (Exception e) {
e.printStackTrace();
}finally{
key = ""; value = "";
enumk = null;vals = null;
clds=null;
props=null;
}
htx = new Hashtable();
htx.putAll(htData);
return htx;
}
public void setUtilsPropDetails(){
HTINITFLENME = getDataProp(INITFILENAME);
this.printAll(HTINITFLENME);
}
public static Hashtable getUtilsPropDetails(){
return HTINITFLENME;
}
#PostConstruct
public void startOnstartup(){
this.setUtilsPropDetails();
this.printAll();
}
#PreDestroy
public void startOnshutdown(){
try {
this.finalize();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
On startup of EJB server "this.printAll(HTINITFLENME);" prints the key values of the XML file hoever If an external Call is made via any other EJB's to the method "getUtilsPropDetails()" does not return the key values....
Am i doing something wrong ??????
Have you considered using the deployment descriptor and having the container do this work for you?
There are of course <resource-ref>, <resource-env-ref>, <ejb-ref> and <env-entry> elements to cover externally configuring which things should be made available to the bean for lookup. For example:
<resource-ref>
<res-ref-name>db</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<mapped-name>java:jdbc/MYSQLDB10</mapped-name>
</resource-ref>
I'm not sure how your vendor handles mapped-name (that particular element is vendor specific), but there will be an equivalent syntax to specify the datasource you want.
The singleton can then lookup java:comp/env/db and return the datasource to other EJBs.
If you are in a compliant Java EE 6 server, then you can change the name to <res-ref-name>java:app/db</res-ref-name> and then anyone in the app can lookup the datasource without the need to get it from the singleton. Global JNDI is a standard feature of Java EE 6 and designed for exactly this.
You can put those elements in the ejb-jar.xml, web.xml or application.xml. Putting them in the application.xml will make the one entry available to the entire application and give you one place to maintain everything.
Global resources can also be injected via:
#Resource(name="java:app/db")
DataSource dataSource;
If for some reason you didn't want to use those, at the very least you could use the <env-entry> element to externalize the strings.
EDIT
See this other answer for a much more complete description of JNDI as it pertains to simple types. This of course can be done where the name/value pairs are not simple types and instead are more complex types like DataSource and Topic or Queue
For example:
<resource-ref>
<res-ref-name>myDataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
<resource-ref>
<res-ref-name>myJmsConnectionFactory</res-ref-name>
<res-type>javax.jms.ConnectionFactory</res-type>
</resource-ref>
<resource-ref>
<res-ref-name>myQueueCF</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
</resource-ref>
<resource-ref>
<res-ref-name>myTopicCF</res-ref-name>
<res-type>javax.jms.TopicConnectionFactory</res-type>
</resource-ref>
<resource-env-ref>
<resource-env-ref-name>myQueue</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>
<resource-env-ref>
<resource-env-ref-name>myTopic</resource-env-ref-name>
<resource-env-ref-type>javax.jms.Topic</resource-env-ref-type>
</resource-env-ref>
<persistence-context-ref>
<persistence-context-ref-name>myEntityManager</persistence-context-ref-name>
<persistence-unit-name>test-unit</persistence-unit-name>
</persistence-context-ref>
<persistence-unit-ref>
<persistence-unit-ref-name>myEntityManagerFactory</persistence-unit-ref-name>
<persistence-unit-name>test-unit</persistence-unit-name>
</persistence-unit-ref>
See the JNDI and simple types answer for look and injection syntax.
I see the name and type, but where's the value?
Configuring what actual things these names refer to has historically been done in a separate vendor specific deployment descriptor, such as sun-ejb-jar.xml or openejb-jar.xml or whatever that vendor requires. The vendor-specific descriptor and the standard ejb-jar.xml descriptor combined provide the guaranteed portability apps require.
The ejb-jar.xml file offering only standard things like being able to say what types of resources the application requires and what names the application has chosen to use to refer to those resources. The vendor-specific descriptor fills the gap of mapping those names to actual resources in the system.
As of EJB 3.0/Java EE 5, we on the spec groups departed from that slightly and added the <mapped-name> element which can be used in the ejb-jar.xml with any of the references shown above, such as <resource-ref>, to the vendor-specific name. Mapped name will never be portable and its value will always be vendor-specific -- if it is supported at all.
That said, <mapped-name> can be convenient in avoiding the need for a separate vendor-specific file and achieves the goal of getting vendors-specific names out of code. After all, the ejb-jar.xml can be edited when moving from one vendor to another and for many people that's good enough.