CCM NG: Basic part of Hibernate Search integration

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4219 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-08-12 18:20:34 +00:00
parent b7458c77a8
commit 790959901b
17 changed files with 271 additions and 16 deletions

View File

@ -62,6 +62,12 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<scope>provided</scope>
</dependency>
<!-- <!--
Flyway framework for database schema migrations Flyway framework for database schema migrations
--> -->

View File

@ -165,7 +165,11 @@ public abstract class AbstractSettingFormSingleValue<T> extends Form {
return; return;
} }
valueField.setValue(state, value.toString()); if (value == null) {
valueField.setValue(state, "");
} else {
valueField.setValue(state, value.toString());
}
} }
} }

View File

@ -302,7 +302,7 @@ public class ConfigurationManager {
"New value of setting \"%s#%s\" is: \"%s\"", "New value of setting \"%s#%s\" is: \"%s\"",
confClassName, confClassName,
settingName, settingName,
value.toString())); value));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final T settingValue = (T) value; final T settingValue = (T) value;
setting.setValue(settingValue); setting.setValue(settingValue);
@ -310,8 +310,7 @@ public class ConfigurationManager {
"Value of setting \"%s#%s\" is now: \"%s\"", "Value of setting \"%s#%s\" is now: \"%s\"",
confClassName, confClassName,
settingName, settingName,
setting.getValue().toString() setting.getValue()));
));
LOGGER.debug("Saving changed setting to DB..."); LOGGER.debug("Saving changed setting to DB...");
settingManager.saveSetting(setting); settingManager.saveSetting(setting);
} }
@ -355,7 +354,7 @@ public class ConfigurationManager {
LOGGER.debug("Setting \"{}#{}\" found. Value: {}", LOGGER.debug("Setting \"{}#{}\" found. Value: {}",
confName, confName,
settingName, settingName,
setting.getValue().toString()); setting.getValue());
field.set(conf, setting.getValue()); field.set(conf, setting.getValue());
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex) {
LOGGER.warn( LOGGER.warn(

View File

@ -176,7 +176,7 @@ public class SettingManager {
if (Strings.isBlank(settingAnnotation.descKey())) { if (Strings.isBlank(settingAnnotation.descKey())) {
settingInfo.setDescKey(String.join(".", settingInfo.setDescKey(String.join(".",
field.getName(), field.getName(),
"descripotion")); "description"));
} else { } else {
settingInfo.setDescKey(settingAnnotation.descKey()); settingInfo.setDescKey(settingAnnotation.descKey());
} }

View File

@ -83,6 +83,7 @@ import org.libreccm.web.ApplicationType;
com.arsdigita.xml.XmlConfig.class, com.arsdigita.xml.XmlConfig.class,
com.arsdigita.xml.formatters.DateFormatterConfig.class, com.arsdigita.xml.formatters.DateFormatterConfig.class,
org.libreccm.configuration.ExampleConfiguration.class, org.libreccm.configuration.ExampleConfiguration.class,
org.libreccm.search.SearchConfig.class,
org.libreccm.security.EmailTemplates.class, org.libreccm.security.EmailTemplates.class,
org.libreccm.security.OneTimeAuthConfig.class,}) org.libreccm.security.OneTimeAuthConfig.class,})
public class CcmCore implements CcmModule { public class CcmCore implements CcmModule {

View File

@ -24,17 +24,19 @@ import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationInfo; import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.internal.util.jdbc.JdbcUtils; import org.flywaydb.core.internal.util.jdbc.JdbcUtils;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.libreccm.search.SearchConfig;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -65,7 +67,7 @@ public class CcmIntegrator implements Integrator {
/** /**
* Service loader containing all modules. Initialised by the * Service loader containing all modules. Initialised by the
* {@link #integrate(Configuration, SessionFactoryImplementor, SessionFactoryServiceRegistry)} * {@link #integrate(Metadata, SessionFactoryImplementor, SessionFactoryServiceRegistry)}
* method. * method.
*/ */
private ServiceLoader<CcmModule> modules; private ServiceLoader<CcmModule> modules;
@ -132,12 +134,10 @@ public class CcmIntegrator implements Integrator {
node.getModuleInfo().getModuleName(), node.getModuleInfo().getModuleName(),
node.getModuleInfo().getModuleVersion()); node.getModuleInfo().getModuleVersion());
migrateModule(node.getModule().getClass(), dataSource); migrateModule(node.getModule().getClass(), dataSource);
// for (Class<?> entity : node.getModuleInfo().getModuleEntities()) {
// configuration.addAnnotatedClass(entity);
// }
} }
configureHibernateSearch(connection, sessionFactory);
} catch (DependencyException | SQLException ex) { } catch (DependencyException | SQLException ex) {
throw new IntegrationException("Failed to integrate modules", ex); throw new IntegrationException("Failed to integrate modules", ex);
} finally { } finally {
@ -145,6 +145,7 @@ public class CcmIntegrator implements Integrator {
} }
LOGGER.info("All modules integrated successfully."); LOGGER.info("All modules integrated successfully.");
} }
/** /**
@ -415,4 +416,61 @@ public class CcmIntegrator implements Integrator {
} }
} }
private void configureHibernateSearch(
final Connection connection,
final SessionFactoryImplementor sessionFactory) throws SQLException {
LOGGER.info("Configuring Hibernate Search...");
LOGGER.debug(
"Checking for Directory Provider setting in configuration...");
final Optional<String> directoryProvider = getSetting(
connection,
SearchConfig.class.getName(),
SearchConfig.DIRECTORY_PROVIDER);
if (directoryProvider.isPresent()) {
LOGGER.debug("Found setting for directory provider: {}",
directoryProvider.orElse(""));
sessionFactory.getProperties().setProperty(
"hibernate.search.default.directory_provider",
directoryProvider.get());
} else {
LOGGER.debug("No setting for directory provider. "
+ "Defaulting to RAM directory provider.");
sessionFactory.getProperties().setProperty(
"hibernate.search.default.directory_provider", "ram");
}
final Optional<String> indexBase = getSetting(
connection,
SearchConfig.class.getName(),
SearchConfig.INDEX_BASE);
if (indexBase.isPresent()) {
LOGGER.debug("Setting Index Base to \"{}\".", indexBase.get());
sessionFactory.getProperties().setProperty(
"hibernate.search.default.indexBase",
indexBase.get());
}
}
private Optional<String> getSetting(final Connection connection,
final String settingClass,
final String settingName)
throws SQLException {
try (PreparedStatement statement = connection.prepareStatement(
"SELECT setting_value_string FROM ccm_core.settings "
+ "WHERE configuration_class = ? AND name = ?")) {
statement.setString(1, settingClass);
statement.setString(2, settingName);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return Optional.ofNullable(resultSet.getString(1));
} else {
return Optional.empty();
}
}
}
}
} }

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.search;
import org.libreccm.configuration.Configuration;
import org.libreccm.configuration.Setting;
import java.util.Objects;
/**
* Configuration for Hibernate Search. Some of the options in this configuration
* are directly applied to Hibernate search. Please refer to the Hibernate
* Search documentation for details and valid values.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Configuration
public final class SearchConfig {
public static final String DIRECTORY_PROVIDER = "directoryProvider";
public static final String INDEX_BASE = "indexBase";
@Setting
private String directoryProvider;
@Setting
private String indexBase;
public String getDirectoryProvider() {
return directoryProvider;
}
public void setDirectoryProvider(final String directoryProvider) {
this.directoryProvider = directoryProvider;
}
public String getIndexBase() {
return indexBase;
}
public void setIndexBase(final String indexBase) {
this.indexBase = indexBase;
}
@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + Objects.hashCode(directoryProvider);
hash = 83 * hash + Objects.hashCode(indexBase);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof SearchConfig)) {
return false;
}
final SearchConfig other = (SearchConfig) obj;
if (!Objects.equals(this.directoryProvider,
other.getDirectoryProvider())) {
return false;
}
return Objects.equals(this.indexBase, other.getIndexBase());
}
@Override
public String toString() {
return String.format("%s{ "
+ "directoryProvider = \"%s\", "
+ "indexBase = \"%s\""
+ " }",
super.toString(),
directoryProvider,
indexBase);
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
/**
* Provides configuration and other services for accessing Hibernate Search.
*/
package org.libreccm.search;

View File

@ -262,15 +262,22 @@ public class Role implements Serializable {
@Override @Override
public String toString() { public String toString() {
// return String.format("%s{ "
// + "roldId = %d, "
// + "name = \"%s\", "
// + "permissions = { %s }"
// + " }",
// super.toString(),
// roleId,
// name,
// Objects.toString(permissions));
return String.format("%s{ " return String.format("%s{ "
+ "roldId = %d, " + "roldId = %d, "
+ "name = \"%s\", " + "name = \"%s\", "
+ "permissions = { %s }"
+ " }", + " }",
super.toString(), super.toString(),
roleId, roleId,
name, name);
Objects.toString(permissions));
} }
} }

View File

@ -43,6 +43,8 @@ public class RolesController {
private final LazyDataModel<Role> tableModel; private final LazyDataModel<Role> tableModel;
private Role selectedRole;
public RolesController() { public RolesController() {
tableModel = new RolesTableModel(); tableModel = new RolesTableModel();
} }
@ -55,6 +57,16 @@ public class RolesController {
return roleRepo.findAll(); return roleRepo.findAll();
} }
public Role getSelectedRole() {
return selectedRole;
}
public void setSelectedRole(final Role selectedRole) {
this.selectedRole = selectedRole;
}
private class RolesTableModel extends LazyDataModel<Role> { private class RolesTableModel extends LazyDataModel<Role> {
private static final long serialVersionUID = 8878060757439667086L; private static final long serialVersionUID = 8878060757439667086L;

View File

@ -64,7 +64,7 @@
<p:tab title="Roles"> <p:tab title="Roles">
<!--<h:outputText value="Roles Placeholder" />--> <!--<h:outputText value="Roles Placeholder" />-->
<!--<p:panel header="#{texts['ui.admin.tab.users_groups_roles.title']}">--> <!--<p:panel header="#{texts['ui.admin.tab.users_groups_roles.title']}">-->
<h:form> <h:form id="rolesForm">
<p:dataTable id="rolesTable" <p:dataTable id="rolesTable"
lazy="true" lazy="true"
paginator="true" paginator="true"
@ -82,12 +82,38 @@
id="role-name"> id="role-name">
<h:outputText value="#{role.name}" /> <h:outputText value="#{role.name}" />
</p:column> </p:column>
<p:column>
<p:commandButton icon="ui-icon-wrench"
oncomplete="PF('roleDialog').show()"
title="Edit"
action="#{rolesController.setSelectedRole(role)}"
>
<!-- -->
<!-- update="selectedRoleName" -->
<!--<f:setPropertyActionListener value="{role}"
target="{rolesController.selectedRole}" />-->
</p:commandButton>
</p:column>
</p:dataTable> </p:dataTable>
<p:dialog header="#{texts['ui.admin.tab.users_groups_roles.edit']}"
widgetVar="roleDialog"
modal="true"
showEffect="fade"
hideEffect="fade">
<p:outputPanel rendered="#{rolesController.selectedRole != null}">
<h:outputText id="selectedRoleName"
rendered="#{rolesController.selectedRole != null}"
value="#{rolesController.selectedRole.name}" />
</p:outputPanel>
</p:dialog>
</h:form> </h:form>
<!--</p:panel>--> <!--</p:panel>-->
</p:tab> </p:tab>
</p:tabView> </p:tabView>
</p:tab> </p:tab>
<p:tab title="#{texts['ui.admin.tab.categories.title']}"> <p:tab title="#{texts['ui.admin.tab.categories.title']}">
<h:outputText value="Categories Placeholder" /> <h:outputText value="Categories Placeholder" />

View File

@ -546,3 +546,4 @@ ui.admin.applications.new_instance_form.title=Create new instance
ui.admin.applications.instance_table.heading=Instances ui.admin.applications.instance_table.heading=Instances
ui.admin.applications.new_instance.primary_url=Primary URL ui.admin.applications.new_instance.primary_url=Primary URL
ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty. ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty.
ui.admin.tab.users_groups_roles.edit=Edit role

View File

@ -550,3 +550,4 @@ ui.admin.applications.new_instance_form.title=Neue Instanz anlegen
ui.admin.applications.instance_table.heading=Instanzen ui.admin.applications.instance_table.heading=Instanzen
ui.admin.applications.new_instance.primary_url=Prim\u00e4re URL ui.admin.applications.new_instance.primary_url=Prim\u00e4re URL
ui.admin.applications.new_instance.primary_url.error.not_empty=Die prim\u00e4re URL einer Applikations-Instanz kann nicht leer sein. ui.admin.applications.new_instance.primary_url.error.not_empty=Die prim\u00e4re URL einer Applikations-Instanz kann nicht leer sein.
ui.admin.tab.users_groups_roles.edit=Rolle bearbeiten

View File

@ -543,3 +543,4 @@ ui.admin.applications.new_instance_form.title=Create new instance
ui.admin.applications.instance_table.heading=Instances ui.admin.applications.instance_table.heading=Instances
ui.admin.applications.new_instance.primary_url=Primary URL ui.admin.applications.new_instance.primary_url=Primary URL
ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty. ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty.
ui.admin.tab.users_groups_roles.edit=Edit role

View File

@ -534,3 +534,4 @@ ui.admin.applications.new_instance_form.title=Create new instance
ui.admin.applications.instance_table.heading=Instances ui.admin.applications.instance_table.heading=Instances
ui.admin.applications.new_instance.primary_url=Primary URL ui.admin.applications.new_instance.primary_url=Primary URL
ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty. ui.admin.applications.new_instance.primary_url.error.not_empty=The primary URL of an application instance can't be empty.
ui.admin.tab.users_groups_roles.edit=Edit role

View File

@ -0,0 +1,7 @@
description = Configuration for Hibernate Search. Some of the options here are directly applied to Hibernate Search. Please refer to the Hibernate Search documentation for details and valid values. Changes to these settings while become effective after a restart of LibreCCM.
directoryProvider.label = Directory Provider
directoryProvider.description = The Lucene Directory provider used by Hibernate Search to store the search index.
indexBase.label = Index Base
indexBase.description = Where the index is stored.

10
pom.xml
View File

@ -270,6 +270,16 @@
<version>5.2.3.Final</version> <version>5.2.3.Final</version>
</dependency> </dependency>
<!--
Hibernate Search provides seamless integration of Apache Lucene with
Hibernate/JPA
-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>5.5.4.Final</version>
</dependency>
<!-- <!--
Flyway framework for database schema migrations Flyway framework for database schema migrations
--> -->