I'm creating some simple spring project (spring security) with a configuration file determining some simple values like table names etc. I defined there a boolean field REQUIRED_ACTIVATION for determining if new user have to activate his account with activation link sent for example via mail.
public static final boolean REQUIRED_ACTIVATION = true;
If I set REQUIRED_ACTIVATION value to false user is active immediately after registration. I've got defined entity which contains data for activation links:
#Entity
#Table(name = 'user_activation_link')
public class UserActivationLink {
[...]
}
When REQUIRED_ACTIVATION is set to false I'm not using this class anywhere but the table is being created in database. Is there any solution for determining if table will be created dependent on value of REQUIRED_ACTIVATION?
You need to do something like this to exclude the tables you don't want to create in the database.
Implement the SchemaFilterProvider and the SchemaFilter interfaces
In the SchemaFilter implementation, add an if condition to includeTable so that it returns false for the table that you don’t
want to create
Add hibernate.properties to the classpath and define hibernate.hbm2ddl.schema_filter_provider to point to the
SchemaFilterProvider implementation
hibernate.hbm2ddl.schema_filter_provider=com.your.package.Provider
And the also:
package com.your.package;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.mapping.Table;
import org.hibernate.tool.schema.spi.SchemaFilter;
import org.hibernate.tool.schema.spi.SchemaFilterProvider;
public class Provider implements SchemaFilterProvider {
#Override
public SchemaFilter getCreateFilter() {
return MySchemaFilter.INSTANCE;
}
#Override
public SchemaFilter getDropFilter() {
return MySchemaFilter.INSTANCE;
}
#Override
public SchemaFilter getMigrateFilter() {
return MySchemaFilter.INSTANCE;
}
#Override
public SchemaFilter getValidateFilter() {
return MySchemaFilter.INSTANCE;
}
}
class MySchemaFilter implements SchemaFilter {
public static final MySchemaFilter INSTANCE = new MySchemaFilter();
#Override
public boolean includeNamespace(Namespace namespace) {
return true;
}
#Override
public boolean includeTable(Table table) {
if (//REQUIRED_ACTIVATION==true && table.getName() is the table you want to exclude){
return false;
}
return true;
}
#Override
public boolean includeSequence(Sequence sequence) {
return true;
}
}
If you use Hibernate with spring.jpa.hibernate.ddl-auto = create / create-drop, so it would try to create a table if not exists on each startup, if you use update it create once if not exists, and after only update table on each startup.
As an alternative try to use Application Listener in Spring:
#Value("${required.activation}")
private Boolean isActivationRequired;
#PersistenceContext
private EntityManager em;
#EventListener
public void handleContextRefreshEvent(ContextRefreshedEvent ctxRefreshedEvent) {
if (!isActivationRequired) {
em.createNativeQuery("drop table user_activation_link").executeUpdate();
}
}
Note here is using #Value instead of public static final field, you can add the field called required.activation in application.properties. It will be automatically injected into private field.
Related
I've created an entities copier in a project where envers is enable but for this copier I don't need the auditing: is there a way to disable temporarily the envers auditing?
I know that there are listener that works as interceptors (before the audit trigger) but I need also know where auditing is triggered from (for example the controller that call the service where auditing is triggered).
I don't know if it's possible.
Thanks
Envers registers a set of event listeners that are triggered by Hibernates Event system.
In order to implement conditional auditing (see envers documentation), you need to replace these listeners with your own implementation. The following event types trigger enver's listeners:
EventType.POST_INSERT
EventType.PRE_UPDATE
EventType.POST_UPDATE
EventType.POST_DELETE
EventType.POST_COLLECTION_RECREATE
EventType.PRE_COLLECTION_REMOVE
EventType.PRE_COLLECTION_UPDATE
Since you want to skip auditing only on copied entities you only need to replace org.hibernate.envers.event.spi.EnversPostInsertEventListenerImpl.
In your entity you add a transient field that acts as a flag. This way you can determine within your listener implementation if the entity was copied.
public class YourEntity {
#Transient
private boolean copy;
public boolean isCopy() {
return copy;
}
public void setCopy(boolean copy) {
this.copy = copy;
}
}
Then you implement your listener by extending the default one:
public class CustomPostInsertListener extends EnversPostInsertEventListenerImpl {
public CustomPostInsertListener(EnversService enversService) {
super(enversService);
}
#Override public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof YourEntity
&& ((YourEntity) event.getEntity()).isCopy()) {
// Ignore this entity
return;
}
super.onPostInsert(event);
}
}
In your copier you need to set the copy flag for your entities.
public YourEntity copyEntity(YourEntity entityToCopy){
YourEntity newEntity;
//... your copy logic
newEntity.setCopy(true);
return newEntity;
}
Then you need to create your own implementation of org.hibernate.integrator.spi.Integrator. The Easiest is to extend org.hibernate.envers.event.spi.EnversIntegrator:
package com.your.project.audit;
public class EnversCustomIntegrator extends EnversIntegrator {
public static final String AUTO_REGISTER = "hibernate.listeners.envers.autoRegister";
private AuditConfiguration enversConfiguration;
#Override
public void integrate(org.hibernate.cfg.Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry listenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);
listenerRegistry.addDuplicationStrategy(EnversListenerDuplicationStrategy.INSTANCE);
enversConfiguration = AuditConfiguration.getFor(configuration, serviceRegistry.getService(ClassLoaderService.class));
if (enversConfiguration.getEntCfg().hasAuditedEntities()) {
listenerRegistry.appendListeners(EventType.POST_DELETE, new EnversPostDeleteEventListenerImpl(enversConfiguration));
listenerRegistry.appendListeners(EventType.POST_INSERT, new CustomPostInsertListener(enversConfiguration));
listenerRegistry.appendListeners(EventType.POST_UPDATE, new EnversPostUpdateEventListenerImpl(enversConfiguration));
listenerRegistry.appendListeners(EventType.POST_COLLECTION_RECREATE, new EnversPostCollectionRecreateEventListenerImpl(enversConfiguration));
listenerRegistry.appendListeners(EventType.PRE_COLLECTION_REMOVE, new EnversPreCollectionRemoveEventListenerImpl(enversConfiguration));
listenerRegistry.appendListeners(EventType.PRE_COLLECTION_UPDATE, new EnversPreCollectionUpdateEventListenerImpl(enversConfiguration));
}
}
}
Lastly you need to add your Integrator implementation in the META-INF/services/org.hibernate.integrator.spi.Integrator file.
com.your.project.audit.EnversCustomIntegrator
I am trying configuring multiple couchbase data source using springboot-data-couchbase.
This is a way I tried to attach two couchbase sources with 2 repositories.
#Configuration
#EnableCouchbaseRepositories("com.xyz.abc")
public class AbcDatasource extends AbstractCouchbaseConfiguration {
#Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("ip_address_of_couchbase");
}
//bucket_name
#Override
protected String getBucketName() {
return "bucket_name";
}
//password
#Override
protected String getBucketPassword() {
return "user_password";
}
#Override
#Bean(destroyMethod = "disconnect", name = "COUCHBASE_CLUSTER_2")
public Cluster couchbaseCluster() throws Exception {
return CouchbaseCluster.create(couchbaseEnvironment(), "ip_address_of_couchbase");
}
#Bean( name = "BUCKET2")
public Bucket bucket2() throws Exception {
return this.couchbaseCluster().openBucket("bucket2", "somepassword");
}
#Bean( name = "BUCKET2_TEMPLATE")
public CouchbaseTemplate newTemplateForBucket2() throws Exception {
CouchbaseTemplate template = new CouchbaseTemplate(
couchbaseClusterInfo(), //reuse the default bean
bucket2(), //the bucket is non-default
mappingCouchbaseConverter(), translationService()
);
template.setDefaultConsistency(getDefaultConsistency());
return template;
}
#Override
public void configureRepositoryOperationsMapping(RepositoryOperationsMapping baseMapping) {
baseMapping
.mapEntity(SomeDAOUsedInSomeRepository.class, newTemplateForBucket2());
}
}
similarly:
#Configuration
#EnableCouchbaseRepositories("com.xyz.mln")
public class MlnDatasource extends AbstractCouchbaseConfiguration {...}
Now the problem is there is no straight forward way to specify namespace based datasource by attaching different beans to these configurations like in springdata-jpa as springdata-jpa support this feature do using entity-manager-factory-ref and transaction-manager-ref.
Due to which only one configuration is being picked whoever comes first.
Any suggestion is greatly appreciated.
Related question: Use Spring Data Couchbase to connect to different Couchbase clusters
#anshul you are almost there.
Make one of the Data Source as #primary which will be used as by default bucket.
Wherever you want to use the other bucket .Just use specific bean in your service class with the qualifier below is the example:
#Qualifier(value = "BUCKET1_TEMPLATE")
#Autowired
CouchbaseTemplate couchbaseTemplate;
Now you can use this template to perform all couch related operations on the desired bucket.
I would want to enable my new feature based on a database value . If the database value is set for the feature , the new code should be enabled. Else it should toggle and flow to the old code. is there a way to accomplish this in Java/Spring ? I do not want to hit the database frequently.I am thinking of one call at the start of the request . Are there any examples to this ? If so please let me know. Thanks
Create Feature class implementation:
public enum CustomFeatures implements Feature {
#Label("Activates customFeature")
MY_CUSTOM_FEATURE;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
Create feature provider:
#Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(CustomFeatures.class);
}
Create entity and repository:
#Entity
public class Feature {
private String name;
private Boolean active;
// getters-setters
}
Create #Component which will query database and sets new feature sate
#Component
public class FeatureToggler {
private final FeatureRepository featureRepository;
private final FeatureManager featureManager;
private FeatureToggler(FeatureRepository featureRepository, FeatureManager featureManager) {
this.featureRepository = featureRepository;
this.featureManager = featureManager;
}
#Schedule(fixedRate=60000)
public void refreshFeatureToggles() {
featureManager.setFeatureState(new FeatureState(CustomFeatures.MY_CUSTOM_FEATURE, featureRepository.findByName().isActive);
}
}
Now you could use check if feature enabled like this:
if(CustomFeatures.MY_CUSTOM_FEATURE.isActive()) {
}
Or use Spring aspect..
I have a problem with isCallerInRole method from the SessionContext. The application run on Glassfish 2.1 and uses JDK6.
In the following class, I'm exposing two methods that ascertain if the current user has a specific role or not. In particular, I'm going to check for two specific roles, say Role1 and Role2. CheckRemote is the interface for CheckFacade.
#Stateless
#DeclareRoles({"Role1", "Role2"})
public class CheckFacade implements CheckRemote {
#Resource
private SessionContext ctx;
#Override
public Boolean hasRole1() {
return ctx.isCallerInRole("Role1");
}
#Override
public Boolean hasRole2() {
return ctx.isCallerInRole("Role2");
}
}
When I go to call hasRole1() or hasRole2() I always get false as result, even if the user has the role.
Now, if I consider the following version that is focused only on one role, then everything works fine, i.e. I get false or true if the user has not the role or has the role, respectively.
#Stateless
#DeclareRoles("Role1")
public class CheckFacade implements CheckRemote {
#Resource
private SessionContext ctx;
#Override
public Boolean hasRole1() {
return ctx.isCallerInRole("Role1");
}
}
Maybe I'm missing something. Any idea?
I currently work in a project where each user has his own schema in Oracle 11g DB. This design was done because all the security roles and privileges to access the Oracle tables are stored in the DB. Our team is trying to figure how to do this in the cool Play! framework. Any suggestions?
As far as I understand, you can try wrap a value of DB.datasource with something like this (where currentUser() and currentPassword() should return the credentials of the current user who makes a request):
public class DataSourceWrapper {
private DataSource original;
public DataSourceWrapper(DataSource original) {
this.original = original;
}
public Connection getConnection() {
return original.getConnection(currentUser(), currentPassword());
}
...
public DataSource getOriginal() {
return original;
}
}
Replacement of DB.datasource should happen between execution of onApplicationStart() methods of DB and JPA plugins, so that you need to create a custom plugin:
public class DataSourceReplacementPlugin extends PlayPlugin {
public void onApplicationStart() {
DB.datasource = new DataSourceWrapper(DB.datasource);
}
public void onApplicationStop() {
if (DB.datasource instanceof DataSourceWrapper) {
DB.datasource = ((DataSourceWrapper) DB.datasource).getOriginal();
}
}
}
and register in with the appropriate priority level in conf/play.plugins:
350: DataSourceReplacementPlugin