Tuesday, January 10, 2012

Using a MultpartRequestResolver with Spring and using Spring Security concurrently

When using Spring Security, the CommonsMultipartResolver will not work. Why? Because the MultipartHttpServletRequest will be wrapped in a SecurityContextHolderAwareRequestWrapper, and will not be matched.

Of course, we don't want to fall back to just taking an HttpServletRequest as a parameter in our RequestMapping and parsing it out, we need to work smarter than that!

The best solution I could come up with is registering a custom WebArgumentResolver (below). But any readers out there have a better solution, please share!

Resolver:

public class SecurityContextWrappedMultipartRequestArgumentResolver implements WebArgumentResolver {

    private final CommonsMultipartResolver commonsMultipartResolver;

    public SecurityContextWrappedMultipartRequestArgumentResolver(){
        this.commonsMultipartResolver = new CommonsMultipartResolver();
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
        if (MultipartHttpServletRequest.class.equals(methodParameter.getParameterType())) {
            Object object = webRequest.getNativeRequest();
            if (! (object instanceof SecurityContextHolderAwareRequestWrapper)) {
                return UNRESOLVED;
            }
            HttpServletRequest request = (HttpServletRequest) object;
            if (!ServletFileUpload.isMultipartContent(request)) {
                return UNRESOLVED;
            }
            SecurityContextHolderAwareRequestWrapper requestWrapper = (SecurityContextHolderAwareRequestWrapper) request;
            return commonsMultipartResolver.resolveMultipart(requestWrapper);
        }
        return UNRESOLVED;
    }
}

Then we just wire it up into our HandlerAdapter (your config may vary):

    @Bean
    public HandlerAdapter handlerAdapter() {
        final AnnotationMethodHandlerAdapter handlerAdapter = new AnnotationMethodHandlerAdapter();
        handlerAdapter.setCustomArgumentResolver(new SecurityContextWrappedMultipartRequestArgumentResolver());
        handlerAdapter.setAlwaysUseFullPath(true);
        List<HttpMessageConverter<?>> converterList = new ArrayList<HttpMessageConverter<?>>();
        converterList.addAll(Arrays.asList(handlerAdapter.getMessageConverters()));
        converterList.add(jibxHttpMessageConverter);
        converterList.add(gsonHttpMessageConverter);
        converterList.add(csvLocalizationConverter);
        converterList.add(protobufLocalizationConverter);
        handlerAdapter.setMessageConverters(converterList.toArray(new HttpMessageConverter[converterList.size()]));
        return handlerAdapter;
    }

Tuesday, November 22, 2011

Hibernate, ElementCollection, and Transactions

Hibernate implemented @ElementCollection in the JPA by binding the persistence of the ElementCollection of a new entity at the end of the transaction, and NOT at the time you tell the EntityManager to persist.  Under most use cases, this should not be a problem, however it does mean that you cannot detach the entity from the EntityManager prior to ending the Transaction.

For example, the following will not persist your ElementCollection.

@Entity
@Table(name = "ParentEntity")
public class ParentEntity {

    @Id
    @GeneratedValue
    @Column(name= "ParentId")
    private long parentId;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "ChildrenNames", joinColumns = @JoinColumn(name = "ParentId"))
    @Column(name = "ChildName")
    private Set<String> children;

    public Set<String> getChildren() {
        return children;
    }

    public void setChildren(Set<String> children) {
        this.children = children;
    }

    @Column(name = "ParentName")
    private String parentName;

    public String getParentName() {
        return parentName;
    }

    public void setParentName(String parentName) {
        this.parentName = parentName;
    }
}
@Service
public class ParentServiceImpl implements ParentService {

    @Autowired
    ParentRepository parentRepository;

    @Transactional
    public void saveParent(ParentEntity parentEntity){
        parentRepository.saveParent(parentEntity);
        parentRepository.detach(parentEntity);
    }
}
@Repository
public class ParentRepository {

    @PersistenceContext

    EntityManager entityManager;

    public void saveParent(ParentEntity parentEntity){
        entityManager.persist(parentEntity);
    }

    public void detachParent(ParentEntity parentEntity){
        entityManager.detach(parentEntity);
    }

    public ParentEntity getParentByName(String parentName){
        TypedQuery<ParentEntity> query = entityManager.createQuery("SELECT p FROM ParentEntity p WHERE p.parentName = :parentName", ParentEntity.class);
        query.setParameter("parentName", parentName);
        return query.getSingleResult();
    }
}

You will likely not run into situations like this, however I'm posting as I ran into some code that I was refactoring for Spring 3.1.  3.1 did not like nested @Transactions on a particular thread, and in the code's original design, it was detaching the entity on the nested item to avoid conflicts, I removed the nested @Transaction but did not notice the detach, and spent days figuring out why hibernate was not persisting the collection.  The answer is, as stated above, hibernate does not persist the collection at .persist, but on commit.

CIFS share accessed in Linux returning 'cannot allocate memory'?

http://www.codenition.com/solving-cannot-allocate-memory-error-on-windows-7-linux-cifs-mounts
From the blog post:
Set the following registry key to ’1′:
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\LargeSystemCache
and set the following registry key to ’3′:
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\Size

Exporting all tables to CSV in SQL Server

The following script will output a list of statements to use bcp to export all tables to CSV. To use another format, see the bcp documentation.
USE vidicom
SELECT 'exec master..xp_cmdshell'
+ ' '''
+ 'bcp'
+ ' ' + TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME
+ ' out'
+ ' E:\releasedDB\VV_6.1\prod\'
+ TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME + '.csv'
+ ' -c'
+ ' -t,'
+ ' -T'
+ ' -S' + @@SERVERNAME
+ ''''
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'

Deleting a database remotely with SQL Server

This should never be done on a production server, as it will open up security risks. This is useful for integration tests and utilities. Replace %dbName% appropriately
BEGIN
 IF EXISTS (SELECT * FROM tempdb.sys.tables WHERE name LIKE '#dbFiles%')
  DROP TABLE #dbFiles
END

EXEC master.dbo.sp_configure 'show advanced options', 1
RECONFIGURE
EXEC master.dbo.sp_configure 'xp_cmdshell', 1
RECONFIGURE

DECLARE @dbFile VARCHAR(8000)
DECLARE @cmd VARCHAR(8000)
SELECT physical_name INTO #dbFiles FROM %dbName%.sys.database_files

ALTER DATABASE %dbName% SET OFFLINE WITH ROLLBACK IMMEDIATE
DROP DATABASE %dbName%

SELECT * FROM #dbFiles

DECLARE cur CURSOR LOCAL FOR
 SELECT physical_name FROM #dbFiles
 
OPEN cur

FETCH next FROM cur INTO @dbFile

WHILE @@FETCH_STATUS = 0 BEGIN
 SET @cmd = 'del "' + @dbFile + '"'
 EXEC master.dbo.xp_cmdshell @cmd
 FETCH next FROM cur INTO @dbFile
END
CLOSE cur
DEALLOCATE cur
DROP TABLE #dbFiles