I want to filter object properties based on authentication or even roles.
So, for example full user profile will be returned for authenticated user and filterd for non authenticated.
How can I achieve it with MappingJacksonHttpMessageConverter? I have already declared custom beans for Jaskon:
<bean id="objectMapper" class="com.example.CustomObjectMapper"/>
<bean id="MappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="order" value="1" />
<!-- <property name="customArgumentResolver" ref="sessionParamResolver"/> -->
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- <property name="conversionService" ref="conversionService" /> -->
<!-- <property name="validator" ref="validator" /> -->
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<ref bean="MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
Note: In controllers I am writing results as:
public void writeJson (Object jsonBean, HttpServletResponse response) {
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
if (jsonConverter.canWrite(jsonBean.getClass(), jsonMimeType)) {
try {
jsonConverter.write(jsonBean, jsonMimeType, new ServletServerHttpResponse(response));
} catch (IOException m_Ioe) {
} catch (HttpMessageNotWritableException p_Nwe) {
} catch (Exception e) {
e.printStackTrace();
}
} else {
log.info("json Converter cant write class " +jsonBean.getClass() );
}
}
If you're wanting to return two separate types of JSON objects (e.g. fullProfile and partialProfile), then you would be best-off making two different services with two different urls. Then you could control access to those urls in the normal manner with Spring Security's intercept-url tags.
I did most of that here https://stackoverflow.com/a/39168090/6761668
All you need to do is pencil in your own security rules, perhaps injecting the current user and deciding what to include or not based on their role. I used an annotation on the entity column:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
#Retention(RetentionPolicy.RUNTIME)
public #interface MyRestricted {
String[] permittedRoles() default {};
}
The column looked like this:
#Column(name = "DISCOUNT_RATE", columnDefinition = "decimal", precision = 7, scale = 2)
#MyRestricted(permittedRoles = { "accountsAdmin", "accountsSuperUser" })
private BigDecimal discountRate;
The rules looked like this:
final MyRestricted roleRestrictedProperty = pWriter.findAnnotation(MyRestricted.class);
if (roleRestrictedProperty == null) {
// public item
super.serializeAsField(pPojo, pJgen, pProvider, pWriter);
return;
}
// restricted - are we in role?
if (permittedRoles.contains(myRole)) {
super.serializeAsField(pPojo, pJgen, pProvider, pWriter);
return;
}
// Its a restricted item for ME
pWriter.serializeAsOmittedField(pPojo, pJgen, pProvider);
Related
I am working in broadleaf which is based on spring-mvc.
there are 3-4 blCustomPersistenceHandlers bean definition in different xml file based on project module.
<bean id="blCustomPersistenceHandlers" class="org.springframework.beans.factory.config.ListFactoryBean" scope="prototype">
<property name="sourceList">
<list>
<bean class="org.broadleafcommerce.admin.server.service.handler.CategoryCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.admin.server.service.handler.CustomerPasswordCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.openadmin.server.security.handler.AdminUserCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.admin.server.service.handler.CustomerCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.admin.server.service.handler.ProductCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.admin.server.service.handler.ChildCategoriesCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.admin.server.service.handler.SkuCustomPersistenceHandler"/>
</list>
</property>
</bean>
below in different xml files
<bean id="blCustomPersistenceHandlers" class="org.springframework.beans.factory.config.ListFactoryBean" scope="prototype">
<property name="sourceList">
<list>
<bean class="org.broadleafcommerce.cms.admin.server.handler.PageTemplateCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.StructuredContentTypeCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.SandBoxItemCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.PendingSandBoxItemCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.TimeDTOCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.RequestDTOCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.StructuredContentItemCriteriaCustomPersistenceHandler"/>
<bean class="org.broadleafcommerce.cms.admin.server.handler.PageItemCriteriaCustomPersistenceHandler"/>
</list>
</property>
</bean>
Above definitions reside into jar files that we included.
Now i want to replace one of this handler , for example ProductCustomPersistenceHandler,
I need to change some functionality regarding that handler, so I changed that handler as below in my xml file.
<bean id="org.broadleafcommerce.admin.server.service.handler.ProductCustomPersistenceHandler"
class="com.mycompany.server.service.handler.HCProductCustomPersistenceHandler" />
and also put bean defination into xml files
<bean id="blCustomPersistenceHandlers" class="org.springframework.beans.factory.config.ListFactoryBean"> <!-- scope="prototype" -->
<property name="sourceList">
<list>
<bean class="com.mycompany.server.service.handler.HCProductCustomPersistenceHandler"/>
</list>
</property>
</bean>
ProductCustomPersistenceHandler class
public class ProductCustomPersistenceHandler extends CustomPersistenceHandlerAdapter {
#Resource(name = "blCatalogService")
protected CatalogService catalogService;
private static final Log LOG = LogFactory.getLog(ProductCustomPersistenceHandler.class);
#Override
public Boolean canHandleAdd(PersistencePackage persistencePackage) {
String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
String[] customCriteria = persistencePackage.getCustomCriteria();
return !ArrayUtils.isEmpty(customCriteria) && "productDirectEdit".equals(customCriteria[0]) && Product.class.getName().equals(ceilingEntityFullyQualifiedClassname);
}
#Override
public Boolean canHandleUpdate(PersistencePackage persistencePackage) {
return canHandleAdd(persistencePackage);
}
#Override
public Entity add(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, RecordHelper helper) throws ServiceException {
Entity entity = persistencePackage.getEntity();
try {
PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
Product adminInstance = (Product) Class.forName(entity.getType()[0]).newInstance();
Map<String, FieldMetadata> adminProperties = helper.getSimpleMergedProperties(Product.class.getName(), persistencePerspective);
adminInstance = (Product) helper.createPopulatedInstance(adminInstance, entity, adminProperties, false);
adminInstance = (Product) dynamicEntityDao.merge(adminInstance);
CategoryProductXref categoryXref = new CategoryProductXrefImpl();
categoryXref.setCategory(adminInstance.getDefaultCategory());
categoryXref.setProduct(adminInstance);
if (adminInstance.getDefaultCategory() != null && !adminInstance.getAllParentCategoryXrefs().contains(categoryXref)) {
categoryXref = (CategoryProductXref) dynamicEntityDao.merge(categoryXref);
adminInstance.getAllParentCategoryXrefs().add(categoryXref);
}
//Since none of the Sku fields are required, it's possible that the user did not fill out
//any Sku fields, and thus a Sku would not be created. Product still needs a default Sku so instantiate one
if (adminInstance.getDefaultSku() == null) {
Sku newSku = catalogService.createSku();
adminInstance.setDefaultSku(newSku);
adminInstance = (Product) dynamicEntityDao.merge(adminInstance);
}
//also set the default product for the Sku
adminInstance.getDefaultSku().setDefaultProduct(adminInstance);
dynamicEntityDao.merge(adminInstance.getDefaultSku());
return helper.getRecord(adminProperties, adminInstance, null, null);
} catch (Exception e) {
LOG.error("Unable to add entity for " + entity.getType()[0], e);
throw new ServiceException("Unable to add entity for " + entity.getType()[0], e);
}
}
#Override
public Entity update(PersistencePackage persistencePackage, DynamicEntityDao dynamicEntityDao, RecordHelper helper) throws ServiceException {
Entity entity = persistencePackage.getEntity();
try {
PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
Map<String, FieldMetadata> adminProperties = helper.getSimpleMergedProperties(Product.class.getName(), persistencePerspective);
Object primaryKey = helper.getPrimaryKey(entity, adminProperties);
Product adminInstance = (Product) dynamicEntityDao.retrieve(Class.forName(entity.getType()[0]), primaryKey);
adminInstance = (Product) helper.createPopulatedInstance(adminInstance, entity, adminProperties, false);
adminInstance = (Product) dynamicEntityDao.merge(adminInstance);
CategoryProductXref categoryXref = new CategoryProductXrefImpl();
categoryXref.setCategory(adminInstance.getDefaultCategory());
categoryXref.setProduct(adminInstance);
if (adminInstance.getDefaultCategory() != null && !adminInstance.getAllParentCategoryXrefs().contains(categoryXref)) {
adminInstance.getAllParentCategoryXrefs().add(categoryXref);
}
return helper.getRecord(adminProperties, adminInstance, null, null);
} catch (Exception e) {
LOG.error("Unable to update entity for " + entity.getType()[0], e);
throw new ServiceException("Unable to update entity for " + entity.getType()[0], e);
}
}
}
I just extend this handler and make my new handler , as it runs only core handler is executing, I want to execute my handler.
But this is not working.
I can't change into core part, so I just need to replace handler with my handler.
How can I do that?
Is that possible in spring?
For custom persistence handlers specifically, you can remove the core handlers by using the blCustomPersistenceHandlerFilters bean. So in your case you would define your beans like this:
<bean id="blCustomPersistenceHandlerFilters" class="org.springframework.beans.factory.config.ListFactoryBean" scope="prototype">
<property name="sourceList">
<list>
<bean class="org.broadleafcommerce.openadmin.server.service.handler.DefaultCustomPersistenceHandlerFilter">
<property name="filterCustomPersistenceHandlerClassnames">
<list>
<value>org.broadleafcommerce.admin.server.service.handler.ProductCustomPersistenceHandler</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
Then you can add your own CPH to the list like you were doing before:
<bean id="blCustomPersistenceHandlers" class="org.springframework.beans.factory.config.ListFactoryBean"> <!-- scope="prototype" -->
<property name="sourceList">
<list>
<bean class="com.mycompany.server.service.handler.HCProductCustomPersistenceHandler"/>
</list>
</property>
</bean>
And now the BLC Product custom persistence handler will not run but yours will.
This is probably a little too complex for your simple purposes of wanting to replace the out-of-the-box one with your custom one. It's possible that there is a good reason why we did it like this, but I added a GitHub Issue for it to investigate further.
I have a problem with method handler return value in spring mvc
I have a grid and when user click on a row I have to show the details of row.
I don't want to send row Id in URL,when I use #PathVariable everything work perfect
I used jqGrid in my jsp to get the Id of selected row
onSelectRow: function(id) {
document.location.href = "/customer/" + id;
}
my controller is :
#RequestMapping("/customer")
#Controller
public class CustomerController {
final Logger logger = LoggerFactory.getLogger(CustomerController.class);
#Autowired
private CustomerService customerService;
#Autowired
MessageSource messageSource;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(Authentication authentication, #PathVariable("id") int id, Model uiModel, Locale locale) {
User user = (User) authentication.getPrincipal();
String result = null;
try {
if (user != null) {
if (user.getRole().getRoleType().equalsIgnoreCase("ADMIN")) {
Customer customer = customerService.getCustomerById(id);
uiModel.addAttribute("customer", customer);
result = "customer/show";
} else
result = "/customer/all";
}
} catch (Exception e) {
uiModel.addAttribute("message", new Message("error",
messageSource.getMessage("customer_get_all_fail", new Object[]{}, locale)));
e.getStackTrace();
}
return result;
}
my apache tiles config for load detail page:
<definition extends="default" name="customer/show">
<put-attribute name="body"
value="/WEB-INF/views/customer/showCustomerData.jspx" />
and my servelt-context.xml
<mvc:annotation-driven/>
<mvc:resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**"/>
<tx:annotation-driven/>
<context:annotation-config/>
<mvc:default-servlet-handler/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="messageSource"
p:basenames="WEB-INF/i18n/messages,WEB-INF/i18n/application" p:fallbackToSystemLocale="false"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<context:component-scan base-package="com.sefryek.sywf.web.controller"/>
<!-- Enable file upload functionality -->
<!--<bean class="org.springframework.web.multipart.support.StandardServletMultipartResolver" id="multipartResolver"/>-->
<!-- Configure the multipart resolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="10000000"/>
</bean>
<bean id="contentNegotiatingResolver"
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order"
value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}"/>
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="pdf" value="application/pdf"/>
<entry key="xsl" value="application/vnd.ms-excel"/>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
<entry key="js" value="application/javascript"/>
</map>
</property>
</bean>
<!--<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"-->
<!--p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:order="#{contentNegotiatingResolver.order+0}"/>-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
p:cookieName="locale" p:cookieMaxAge="11"/>
<!-- Tiles Configuration -->
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/layouts/layouts.xml</value>
<!-- Scan views directory for Tiles configurations -->
<value>/WEB-INF/views/**/views.xml</value>
</list>
</property>
</bean>
<bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver"
p:cookieName="theme" p:defaultThemeName="standard"/>
<bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource"/>
</beans>
and anything work perfect,but I have to send my data in request object instead of in URL
my code
in jsp :
onSelectRow: function(id) {
$.ajax({
url : '${showSelectedCustomer}',
dataType: 'JSON',
data:{ id: id },
type:'POST',
success: function(response) {
} ,
error : function(r) {
}
});
}
and my method handler:
#RequestMapping(value = "/showSelectedCustomer", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
//#ResponseBody
public String showSelectedCustomer(
Authentication authentication,
#RequestParam String id,
Model uiModel, Locale locale) {
User user = (User) authentication.getPrincipal();
Customer customer = null;
Map<String, Object> map = new HashMap<String, Object>();
try {
if (user != null) {
if (user.getRole().getRoleType().equalsIgnoreCase("ADMIN")) {
customer = customerService.getCustomerById(Integer.valueOf(id));
uiModel.addAttribute("customer", customer);
}
}
} catch (Exception e) {
uiModel.addAttribute("message", new Message("error",
messageSource.getMessage("customer_get_all_fail", new Object[]{}, locale)));
e.getStackTrace();
}
return "customer/show";
}
my method handler is called but after that nothing is happened.
my point is showCustomerData.jspx that show a details of customer isn't loaded.
please let me know what should I do and what my problem is?
when I use my data with Ajax,how should I return my return value to see detail page (showCustomerData.jspx)
when I change my method handler to return a map with map.put("message", "success") data,my ajax success function is called but I don't know how can I redirect it to show my details page
thank you
You first need to create CustomerInput pojo like below
class CustomerInput{
private Integer id;
//setter getter
}
Then create controller method like below
#ResponseBody public String showSelectedCustomer( Authentication authentication, #RequestBody CustomerInput customerInput, Model uiModel, Locale locale) {
}
I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I'm trying to use the Spring transaction manager but am having an issue where Spring is returning old data for a find method. In my Spring application, I call a save method, and then a find method. After I call save, I can see the changes in the database, but when I call the find, it is returning the old state of the object. Here is the controller methods ...
// method to save
#RequestMapping(method = RequestMethod.POST)
public String saveUserEventFeeds(final HttpServletRequest request,
#ModelAttribute("eventFeeds") final Set<EventFeed> eventFeeds) {
final String nextPage = "user/eventfeeds";
try {
final String[] eventFeedIds = request.getParameterValues("userEventFeeds");
final Set<EventFeed> userEventFeeds = new HashSet<EventFeed>();
if (eventFeedIds != null) {
for (final String eventFeedId : eventFeedIds) {
final EventFeed eventFeed = getEventFeed(eventFeeds, Integer.parseInt(eventFeedId));
userEventFeeds.add(eventFeed);
} // for
} // if
final Registration currentUser = (Registration) securityContextFacade.getContext().getAuthentication().getPrincipal();
userService.saveUserEventFeeds(currentUser.getId(), userEventFeeds);
} catch (Exception exc) {
LOG.error(exc.getMessage(), exc);
} // try
return nextPage;
} // saveUserEventFeeds
// method to retrieve user
#ModelAttribute("user")
public UserDetails getUser() {
final Registration reg = (Registration) securityContextFacade.getContext().getAuthentication().getPrincipal();
final int id = reg.getId();
final Registration foundUser = userService.findUserById(id);
return (UserDetails) foundUser;
} // getUser
and here is the service where i declare everything transactional ...
#Transactional(rollbackFor = Exception.class)
#Component("userService")
public class UserServiceImpl implements UserService {
...
#Override
public void saveUserEventFeeds(Integer userId, Set<EventFeed> eventFeeds) {
final Registration searchUser = new Registration();
searchUser.setId(userId);
final Registration user = usersDao.getUser(searchUser);
if (user != null) {
user.setUserEventFeeds(eventFeeds);
usersDao.saveUser(user);
} else {
throw new RuntimeException("User with id " + userId + " not found.");
} // if
}
#Override
public Registration findUserById(Integer id) {
final Registration searchUser = new Registration();
if (id != null) {
searchUser.setId(id);
} // if
return usersDao.getUser(searchUser);
}
Below is the transaction manager I've declared in my application context file. If you can see how I can configure things differently so that I can get the most current data on my finds, please let me know.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/myproj"/>
<property name="username" value="myproj"/>
<property name="password" value="password"/>
<property name="maxActive" value="10"/>
<property name="minIdle" value="5"/>
<!-- SELECT 1 is a simple query that returns 1 row in MySQL -->
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" id="sessionFactory">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.myco.myproj.domain.Registration</value>
<value>com.myco.myproj.domain.Role</value>
<value>com.myco.myproj.domain.EventFeed</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="show_sql">true</prop>
<prop key="dialect">org.hibernate.dialect.MySQLDialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<tx:annotation-driven />
We have an application where we have used spring for IOC. We have the dataSource bean configured in applicationContext.xml and that is referenced in other bean definations.
The dataSource bean defination looks like:
<bean id="dbDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url"
value="jdbc:oracle:oci:#TESTDB" />
<property name="username" value="TESTUSER" />
<property name="password" value="TESTPWD" />
<property name="initialSize" value="50" />
<property name="maxActive" value="40" />
<property name="maxIdle" value="10" />
<property name="minIdle" value="10" />
<property name="maxWait" value="-1" />
</bean>
<bean id="serviceDAO" class="com.test.impl.ServiceDAOImpl">
<property name="dataSource" ref="dbDataSource" />
</bean>
ServiceDAOImpl looks as follows:
public class ServiceDAOImpl implements ServiceDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public ValueObj readValue(String key) {
String query = "SELECT * FROM SERVICE_LOOKUP WHERE KEY=?";
/**
* Implement the RowMapper callback interface
*/
return (ValueObj) jdbcTemplate.queryForObject(query,
new Object[] { key }, new RowMapper() {
public Object mapRow(ResultSet resultSet, int rowNum)
throws SQLException {
return new ValueObj(resultSet.getString("KEY"),
resultSet.getString("VALUE"));
}
});
}
public ServiceDAOImpl() {
}
}
Now, at the server start up injection is happening fine and when we use the dataSource in serviceDAOImpl the connection is happening fine. But the very first time the database call is made it takes around 3 mins to get the response back. I think this is because the pool creation is done during the first call and we have set the parameter "initialSize" = 50 in applicationConext.xml.
So, to avoid this we need a way in which the pool can be created during the application startup itself and can be used directly.
Please suggest. Let me know if any clarification required.
Regards
Saroj
There's a work-around for this .You could force jdbcTemplate to use the
DB connection at startup. See the link here for detailed explanation .
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg index="0" ref="dataSource"/>
<constructor-arg index="1" value="false"/>
</bean>
The second constructor-arg is the lazy Init flag.
Aravind A's solution is the preffered one, but just in case you don't want to define an extra bean you can point spring to your DAO's init method:
<bean id="serviceDAO" class="com.test.impl.ServiceDAOImpl" init-method="init">
<property name="dataSource" ref="dbDataSource" />
</bean>
and then define ServiceDAOImpl.init() which calls some sql like SELECT 1 FROM SERVICE_LOOKUP LIMIT 1 or even better some noop like SELECT 1:
public class ServiceDAOImpl implements ServiceDAO {
public void init() {
String query = "SELECT 1 FROM SERVICE_LOOKUP LIMIT 1";
int i = jdbcTemplate.queryForInt(query);
}
}
I'm brand spanking new at Spring and have gotten a majority of the knowledge I do have from the Spring Recipes book from Apress.
I've got LDAP authentication working with Spring Security within one webapp. I would like to rip out my application context beans and properties files from this one webapp, however, and somehow externalize them so that all of our webapps can reference the same beans. So when we need to change something (like the ldapuser or the ldap urls), we change it in one place and the rest of the apps just know.
UPDATE
I've implemented Reloadable Spring Properties which is reloading properties when the files they come from are touched. I am using encrypted properties, however, so below is class I created on top of the Reloadable Spring Properties ones.
ReloadingEncryptablePropertyPlaceholderConfigurer.java
package;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.util.text.TextEncryptor;
import org.jasypt.properties.PropertyValueEncryptionUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
public class ReloadingEncryptablePropertyPlaceholderConfigurer extends ReloadingPropertyPlaceholderConfigurer {
protected final Log logger = LogFactory.getLog(getClass());
private final StringEncryptor stringEncryptor;
private final TextEncryptor textEncryptor;
public ReloadingEncryptablePropertyPlaceholderConfigurer(TextEncryptor textEncryptor) {
super();
logger.info("Creating configurer with TextEncryptor");
Validate.notNull(textEncryptor, "Encryptor cannot be null");
this.stringEncryptor = null;
this.textEncryptor = textEncryptor;
}
public ReloadingEncryptablePropertyPlaceholderConfigurer(StringEncryptor stringEncryptor) {
super();
logger.info("Creating configurer with StringEncryptor");
Validate.notNull(stringEncryptor, "Encryptor cannot be null");
this.stringEncryptor = stringEncryptor;
this.textEncryptor = null;
}
#Override
protected String convertPropertyValue(String originalValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(originalValue)) {
return originalValue;
}
if (this.stringEncryptor != null) {
return PropertyValueEncryptionUtils.decrypt(originalValue, this.stringEncryptor);
}
return PropertyValueEncryptionUtils.decrypt(originalValue, this.textEncryptor);
}
#Override
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders) throws BeanDefinitionStoreException {
return convertPropertyValue(super.parseStringValue(strVal, props, visitedPlaceholders));
}
}
And here's how I use it in my securityContext.xml:
<bean id="securityContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldaps://ldapserver" />
<property name="urls" value="#{ldap.urls}" />
</bean>
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<bean id="reloadProperties" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="period" value="1000"/>
<property name="runnable">
<bean class="ReloadConfiguration">
<property name="reconfigurableBeans">
<list>
<ref bean="configproperties"/>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="configproperties" class="ReloadablePropertiesFactoryBean">
<property name="location" value="classpath:ldap.properties"/>
</bean>
<bean id="ldapPropertyConfigurer" class="ReloadingEncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="properties" ref="configproperties"/>
</bean>
<bean id="jasyptConfig" class="org.jasypt.encryption.pbe.config.SimpleStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndTripleDES" />
<property name="password" value="########" />
</bean>
<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="jasyptConfig" />
</bean>
How about:
Writing a method that returns a list
of LDAP servers - reading from a
database table or property files
expose this wethod via jndi and use it to inject a list of the servers into your spring config
If you need the ldap servers to be refreshed dynamically you could have a job poll for changes periodically or else have an admin webpage or jmx bean to trigger the update. Be careful of concurrency isses for both these methods (something reading the list while you are updating)
Wouldn't that be Spring Security? It can deal with LDAPs. And if you make it one security service that everyone uses, wouldn't that be the way to manage it?