Исходный текст в автономной программе Java
Я использую JNDI для создания пула соединений tomcat. Он отлично работает в веб-приложении. Я считаю, что исходный текст предоставлен сервером tomcat.
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
dataSource = (DataSource)envContext.lookup("jdbc/testdb");
Но когда я пытаюсь вызвать ту же самую утилиту из автономной программы Java, объект initContext имеет значение null. Как я могу явным образом предоставить все необходимые свойства, которые ожидают объекты контекста.
Ошибка: javax.naming.NoInitialContextException: необходимо указать класс имя в среде или системном свойстве или в качестве параметра апплета или в файле ресурсов приложения: java.naming.factory.initial
Ответы
Ответ 1
Вы также можете создать свой собственный контекст.
LocalContext ctx = LocalContextFactory.createLocalContext();
ctx.addDataSource("jdbc/testdb", driverName, url, usr, pwd);
Для получения дополнительной информации см. Запуск локальных компонентов, использующих источники данных сервера приложений.
ОБНОВЛЕНИЕ
Вы можете использовать класс org.springframework.mock.jndi.SimpleNamingContextBuilder
of Spring. например:
-
Настроить:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("jdbc/Oracle", ods);
builder.activate();
-
Использование:
DataSource ds = InitialContext.doLookup("jdbc/Oracle");
Ответ 2
Вот пример, адаптированный из принятого ответа, но выполняющий все inline, чтобы избежать создания дополнительных классов.
public static void main(String[] args) {
setupInitialContext();
//do something that looks up a datasource
}
private static void setupInitialContext() {
try {
NamingManager.setInitialContextFactoryBuilder(new InitialContextFactoryBuilder() {
@Override
public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
return new InitialContextFactory() {
@Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
return new InitialContext(){
private Hashtable<String, DataSource> dataSources = new Hashtable<>();
@Override
public Object lookup(String name) throws NamingException {
if (dataSources.isEmpty()) { //init datasources
MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
ds.setURL("jdbc:mysql://localhost:3306/mydb");
ds.setUser("mydbuser");
ds.setPassword("mydbpass");
dataSources.put("jdbc/mydbname", ds);
//add more datasources to the list as necessary
}
if (dataSources.containsKey(name)) {
return dataSources.get(name);
}
throw new NamingException("Unable to find datasource: "+name);
}
};
}
};
}
});
}
catch (NamingException ne) {
ne.printStackTrace();
}
}
Ответ 3
Нет прямого способа использования Контекста Tomcat Factory, см. здесь для получения дополнительной документации по альтернативам. Но я рекомендую вам попробовать запустить реестр за пределами Tomcat...
// select the registry context factory for creating a context
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
// specify where that factory is running.
env.put(Context.PROVIDER_URL, "rmi://server:1099");
// create an initial context that accesses the registry
Context ctx = new InitialContext(env);
Вы можете изменить свой код в Tomcat, чтобы также использовать этот внешний RegistryContext, и тогда оба набора (ов) использовали бы тот же JNDI-провайдер. Этот question кажется очень похожим.
Ответ 4
Tomcat предоставляет реализации контекста и DataSource, которые работают с классом InitialContext. При запуске Tomcat свойство Context.INITIAL_CONTEXT_FACTORY устанавливается так, чтобы оно указывало на реализацию Tomcat. Когда вы не используете Tomcat, у вас нет этой возможности... вам нужно использовать стороннюю библиотеку, например c3p0 для объединения пулов.
Ответ 5
Вы можете создать начальный контекст, используя код удара.
InitialContext ic = new InitialContext();
// Retrieve the Home interface using JNDI lookup
Object helloObject = ic.lookup("java:comp/env/ejb/HelloBean");
если вы хотите создать собственный начальный контекст, вы можете расширить класс javax.naming.InitailContext
Ответ 6
Вы можете создать свой собственный Context
, подклассифицируя javax.naming.InitialContext
и реализуя только небольшое подмножество методов, обычно это методы bind
и lookup
.
Затем вы можете создать свой источник данных и связать его с вашим начальным контекстом с определенным ключом. После этого вы готовы пойти и запросить из любого места свой JNDI-контекст в вашей автономной Java-программе.
Это код, который вы можете использовать для создания своего собственного контекста:
InitialContext initialContext = new InitialContext() {
private Map<String, Object> table = new HashMap<>();
public void bind(String key, Object value) {
table.put(key, value);
}
public Object lookup(String key) throws NamingException {
return table.get(key);
}
};
// Activate the initial context
NamingManager.setInitialContextFactoryBuilder(environment -> environment1 -> initialContext);
Затем вы можете инициализировать ваш источник данных, какой бы вы ни выбрали:
InitialContext ic = new InitialContext();
BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
bds.setUrl(url);
bds.setUsername(username);
bds.setPassword(password);
ic.bind(jndiPath, bds);
И где-то еще в вашем коде вы можете использовать существующий источник данных, извлекая его из контекста JNDI:
InitialContext ic2 = new InitialContext();
DataSource ds = (DataSource) ic2.lookup(jndiPath);
assertNotNull(ds);
Connection conn = ds.getConnection();
assertNotNull(conn);
conn.close();