Java tutorial
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Content Registry 3 * * The Initial Owner of the Original Code is European Environment * Agency. Portions created by Zero Technologies are Copyright * (C) European Environment Agency. All Rights Reserved. * * Contributor(s): * Jaanus Heinlaid */ package eionet.cr.dao.virtuoso; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.openrdf.OpenRDFException; import org.openrdf.model.Literal; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.model.impl.ContextStatementImpl; import org.openrdf.query.BindingSet; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import eionet.cr.common.Predicates; import eionet.cr.common.Subjects; import eionet.cr.config.GeneralConfig; import eionet.cr.dao.DAOException; import eionet.cr.dao.FolderDAO; import eionet.cr.dao.readers.ResultSetReaderException; import eionet.cr.dao.readers.UserFolderReader; import eionet.cr.dto.FolderItemDTO; import eionet.cr.util.Bindings; import eionet.cr.util.FolderUtil; import eionet.cr.util.Hashes; import eionet.cr.util.Pair; import eionet.cr.util.URIUtil; import eionet.cr.util.URLUtil; import eionet.cr.util.sesame.SPARQLQueryUtil; import eionet.cr.util.sesame.SPARQLResultSetReader; import eionet.cr.util.sesame.SesameUtil; import eionet.cr.util.sql.SQLUtil; import eionet.cr.util.sql.SingleObjectReader; import eionet.cr.web.security.CRUser; /** * Virtuoso implementation for the {@link FolderDAO}. * * @author Jaanus Heinlaid */ public class VirtuosoFolderDAO extends VirtuosoBaseDAO implements FolderDAO { /** */ private static final Logger LOGGER = Logger.getLogger(VirtuosoFolderDAO.class); /** */ private static final String INSERT_NEVER_HARVESTED_SOURCE_SQL = "insert soft HARVEST_SOURCE (URL,URL_HASH,TIME_CREATED,INTERVAL_MINUTES) values (?,?,now(),0)"; /* * (non-Javadoc) * * @see eionet.cr.dao.FolderDAO#createUserHomeFolder(java.lang.String) */ @Override public void createUserHomeFolder(String userName) throws DAOException { if (StringUtils.isBlank(userName)) { throw new IllegalArgumentException("User name must not be blank!"); } CRUser user = new CRUser(userName); Connection sqlConn = null; RepositoryConnection repoConn = null; try { sqlConn = SesameUtil.getSQLConnection(); sqlConn.setAutoCommit(false); repoConn = SesameUtil.getRepositoryConnection(); repoConn.setAutoCommit(false); ValueFactory vf = repoConn.getValueFactory(); List<Statement> statements = getHomeFolderCreationStatements(user, vf); repoConn.add(statements); createNeverHarvestedSources(sqlConn, statements); repoConn.commit(); sqlConn.commit(); } catch (OpenRDFException e) { SesameUtil.rollback(repoConn); throw new DAOException(e.getMessage(), e); } catch (SQLException e) { SQLUtil.rollback(sqlConn); throw new DAOException(e.getMessage(), e); } finally { SQLUtil.close(sqlConn); SesameUtil.close(repoConn); } } /* * (non-Javadoc) * * @see eionet.cr.dao.FolderDAO#createFolder(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ @Override public void createFolder(String parentFolderUri, String folderName, String folderLabel, String homeUri) throws DAOException { // Make sure we have valid inputs. if (StringUtils.isBlank(parentFolderUri) || StringUtils.isBlank(folderName)) { throw new IllegalArgumentException("Parent folder URI and folder name must not be blank!"); } // Remove trailing "/" from parent URI, if such exists if (parentFolderUri.endsWith("/")) { parentFolderUri = StringUtils.substringBeforeLast(parentFolderUri, "/"); } // If the new folder URI is reserved, exit silently. String newFolderUri = parentFolderUri + "/" + URLUtil.escapeIRI(folderName); if (FolderUtil.isUserReservedUri(newFolderUri)) { LOGGER.debug("Cannot create reserved folder, exiting silently!"); return; } Connection sqlConn = null; RepositoryConnection repoConn = null; try { sqlConn = SesameUtil.getSQLConnection(); sqlConn.setAutoCommit(false); repoConn = SesameUtil.getRepositoryConnection(); repoConn.setAutoCommit(false); ValueFactory vf = repoConn.getValueFactory(); URI homeFolder = vf.createURI(homeUri); URI parentFolder = vf.createURI(parentFolderUri); URI hasFolder = vf.createURI(Predicates.CR_HAS_FOLDER); URI newFolder = vf.createURI(newFolderUri); URI rdfType = vf.createURI(Predicates.RDF_TYPE); URI rdfsLabel = vf.createURI(Predicates.RDFS_LABEL); URI allowSubObjectType = vf.createURI(Predicates.CR_ALLOW_SUBOBJECT_TYPE); Literal folderLabelLiteral = vf.createLiteral(folderLabel); URI folder = vf.createURI(Subjects.CR_FOLDER); URI file = vf.createURI(Subjects.CR_FILE); ArrayList<Statement> statements = new ArrayList<Statement>(); statements.add(new ContextStatementImpl(parentFolder, hasFolder, newFolder, homeFolder)); statements.add(new ContextStatementImpl(newFolder, rdfType, folder, homeFolder)); if (StringUtils.isNotEmpty(folderLabel)) { statements.add(new ContextStatementImpl(newFolder, rdfsLabel, folderLabelLiteral, homeFolder)); } statements.add(new ContextStatementImpl(newFolder, allowSubObjectType, folder, homeFolder)); statements.add(new ContextStatementImpl(newFolder, allowSubObjectType, file, homeFolder)); repoConn.add(statements); // if a new project is created add subfolders if (FolderUtil.isProjectRootFolder(parentFolderUri)) { repoConn.add(getProjectFolderCreationStatements(folderName, vf)); } createNeverHarvestedSources(sqlConn, statements); repoConn.commit(); sqlConn.commit(); } catch (OpenRDFException e) { SesameUtil.rollback(repoConn); throw new DAOException(e.getMessage(), e); } catch (SQLException e) { SQLUtil.rollback(sqlConn); throw new DAOException(e.getMessage(), e); } finally { SQLUtil.close(sqlConn); SesameUtil.close(repoConn); } } /* * @see eionet.cr.dao.FolderDAO#folderExists(java.lang.String, java.lang.String) */ @Override public boolean fileOrFolderExists(String parentFolderUri, String folderName) throws DAOException { // Make sure we have valid inputs. if (StringUtils.isBlank(parentFolderUri) || StringUtils.isBlank(folderName)) { throw new IllegalArgumentException("Parent folder URI and folder name must not be blank!"); } // Prepend the parent folder with "/" if it's not done yet. if (!parentFolderUri.endsWith("/")) { parentFolderUri = parentFolderUri + "/"; } return fileOrFolderExists(parentFolderUri + folderName); } /* * (non-Javadoc) * * @see eionet.cr.dao.FolderDAO#fileOrFolderExists(java.lang.String) */ @Override public boolean fileOrFolderExists(String folderUri) throws DAOException { // Make sure we have valid inputs. if (StringUtils.isBlank(folderUri)) { throw new IllegalArgumentException("Folder URI must not be blank!"); } String parentFolderUri = StringUtils.substringBeforeLast(folderUri, "/"); Bindings bindings = new Bindings(); bindings.setURI("parentFolder", parentFolderUri); bindings.setURI("folderUri", folderUri); bindings.setURI("crFolder", Subjects.CR_FOLDER); bindings.setURI("crHasFolder", Predicates.CR_HAS_FOLDER); bindings.setURI("crHasFile", Predicates.CR_HAS_FILE); StringBuilder sb = new StringBuilder(); sb.append(SPARQLQueryUtil.getCrInferenceDefinitionStr()); sb.append("select count(*) where { "); sb.append("?parentFolder ?p ?o . "); sb.append("filter (?p IN (?crHasFolder, ?crHasFile)) . "); sb.append("filter (?o = ?folderUri) } "); String result = executeUniqueResultSPARQL(sb.toString(), bindings, new SingleObjectReader<String>()); if (Integer.parseInt(result) > 0) { return true; } return false; } /** * {@inheritDoc} */ @Override public Pair<FolderItemDTO, List<FolderItemDTO>> getFolderContents(String uri) throws DAOException { FolderItemDTO parentFolder = new FolderItemDTO(); parentFolder.setUri(uri); parentFolder.setName(URIUtil.extractURILabel(parentFolder.getUri())); // Folder contents query Bindings bindings = new Bindings(); bindings.setURI("folderUri", uri); bindings.setURI("hasFile", Predicates.CR_HAS_FILE); bindings.setURI("hasFolder", Predicates.CR_HAS_FOLDER); bindings.setURI("dcTitle", Predicates.DC_TITLE); bindings.setURI("rdfsLabel", Predicates.RDFS_LABEL); bindings.setURI("crLastModified", Predicates.CR_LAST_MODIFIED); StringBuilder sb = new StringBuilder(); sb.append("SELECT ?type, ?item, ?label, ?lastModified WHERE { "); sb.append("?folderUri ?type ?item . "); sb.append("OPTIONAL { ?item ?rdfsLabel ?label } . "); sb.append("OPTIONAL { ?item ?crLastModified ?lastModified } . "); sb.append("FILTER (?type IN (?hasFile, ?hasFolder))"); sb.append("}"); SPARQLResultSetReader<FolderItemDTO> reader = new SPARQLResultSetReader<FolderItemDTO>() { List<FolderItemDTO> result = new ArrayList<FolderItemDTO>(); @Override public List<FolderItemDTO> getResultList() { return result; } @Override public void endResultSet() { } @Override public void startResultSet(List<String> bindingNames) { } @Override public void readRow(BindingSet bindingSet) throws ResultSetReaderException { FolderItemDTO item = new FolderItemDTO(); item.setUri(bindingSet.getValue("item").stringValue()); item.setName(URIUtil.extractURILabel(item.getUri())); if (bindingSet.getValue("label") != null && StringUtils.isNotEmpty(bindingSet.getValue("label").stringValue())) { item.setTitle(bindingSet.getValue("label").stringValue()); } if (bindingSet.getValue("lastModified") != null && StringUtils.isNotEmpty(bindingSet.getValue("lastModified").stringValue())) { item.setLastModified(bindingSet.getValue("lastModified").stringValue()); } if (Predicates.CR_HAS_FOLDER.equals(bindingSet.getValue("type").stringValue())) { if (FolderUtil.isUserReservedUri(item.getUri())) { item.setType(FolderItemDTO.Type.RESERVED_FOLDER); } else { item.setType(FolderItemDTO.Type.FOLDER); } } if (Predicates.CR_HAS_FILE.equals(bindingSet.getValue("type").stringValue())) { if (FolderUtil.isUserReservedUri(item.getUri())) { item.setType(FolderItemDTO.Type.RESERVED_FILE); } else { item.setType(FolderItemDTO.Type.FILE); } } result.add(item); } }; List<FolderItemDTO> items = executeSPARQL(sb.toString(), bindings, reader); Collections.sort(items); return new Pair<FolderItemDTO, List<FolderItemDTO>>(parentFolder, items); } /** * {@inheritDoc} */ @Override public List<String> getSubFolders(String uri) throws DAOException { List<String> ret = new ArrayList<String>(); ret.add(uri); Bindings bindings = new Bindings(); bindings.setURI("uri", uri); bindings.setURI("hasFolder", Predicates.CR_HAS_FOLDER); StringBuilder sb = new StringBuilder(); sb.append("SELECT ?o WHERE { "); sb.append("graph ?g {"); sb.append("?s ?p ?o . "); sb.append("FILTER (?g = ?uri) ."); sb.append("FILTER (?p = ?hasFolder) ."); sb.append("}} ORDER BY ?o"); List<String> folders = executeSPARQL(sb.toString(), bindings, new UserFolderReader()); if (folders != null) { ret.addAll(folders); } return ret; } /** * {@inheritDoc} */ @Override public boolean folderHasItems(String folderUri) throws DAOException { Bindings bindings = new Bindings(); bindings.setURI("folderUri", folderUri); bindings.setURI("hasFile", Predicates.CR_HAS_FILE); bindings.setURI("hasFolder", Predicates.CR_HAS_FOLDER); StringBuilder sb = new StringBuilder(); sb.append("SELECT count(*) WHERE { "); sb.append("?folderUri ?type ?item . "); sb.append("FILTER (?type IN (?hasFile, ?hasFolder))"); sb.append("}"); String result = executeUniqueResultSPARQL(sb.toString(), bindings, new SingleObjectReader<String>()); if (Integer.parseInt(result) > 0) { return true; } return false; } /** * {@inheritDoc} */ @Override public void deleteFileOrFolderUris(String folderUri, List<String> subjectUris) throws DAOException { if (subjectUris == null || subjectUris.size() == 0) { return; } Connection sqlConn = null; RepositoryConnection repoConn = null; try { repoConn = SesameUtil.getRepositoryConnection(); ValueFactory valueFactory = repoConn.getValueFactory(); StringBuilder strBuilder = new StringBuilder(); for (String subjectUri : subjectUris) { URI subjectResource = valueFactory.createURI(subjectUri); URI folderContext = valueFactory.createURI(folderUri); URI harvesterContext = valueFactory.createURI(GeneralConfig.HARVESTER_URI); // JH190511: although Sesame API claims that in RepositoryConnection.remove(...) // the context is optional, Virtuoso requires the context (i.e. the graph) always // to be specified in triple removal commands. Virtuoso Sesame driver seems to // silently ignore the whole command, if no context specified. repoConn.remove(subjectResource, null, null, folderContext, harvesterContext); repoConn.remove(null, null, (Value) subjectResource, folderContext, harvesterContext); if (strBuilder.length() > 0) { strBuilder.append(","); } strBuilder.append(Hashes.spoHash(subjectUri)); } sqlConn = getSQLConnection(); SQLUtil.executeUpdate("delete from SPO_BINARY where SUBJECT in (" + strBuilder + ")", sqlConn); } catch (RepositoryException e) { throw new DAOException(e.toString(), e); } catch (SQLException e) { throw new DAOException(e.toString(), e); } finally { SesameUtil.close(repoConn); SQLUtil.close(sqlConn); } } private List<Statement> getProjectFolderCreationStatements(String projectName, ValueFactory vf) { String projectRootFolder = FolderUtil.getProjectsFolder(); String projectFolder = FolderUtil.getProjectFolder(projectName); String bookmarksFolder = projectFolder + "/bookmarks"; URI projectsRootURI = vf.createURI(projectRootFolder); URI projectHomeUri = vf.createURI(projectFolder); URI rdfType = vf.createURI(Predicates.RDF_TYPE); URI rdfsLabel = vf.createURI(Predicates.RDFS_LABEL); URI hasFile = vf.createURI(Predicates.CR_HAS_FILE); URI bookmarksFile = vf.createURI(Subjects.CR_BOOKMARKS_FILE); URI bookmarksUri = vf.createURI(bookmarksFolder); Literal bookmarksLabel = vf.createLiteral(projectName + "'s bookmarks"); ArrayList<Statement> result = new ArrayList<Statement>(); result.add(new ContextStatementImpl(projectHomeUri, hasFile, bookmarksUri, projectsRootURI)); result.add(new ContextStatementImpl(bookmarksUri, rdfType, bookmarksFile, projectsRootURI)); result.add(new ContextStatementImpl(bookmarksUri, rdfsLabel, bookmarksLabel, projectsRootURI)); return result; } /** * * @param user * @return */ private List<Statement> getHomeFolderCreationStatements(CRUser user, ValueFactory vf) { URI rootHomeUri = vf.createURI(CRUser.rootHomeUri()); URI rootProjectUri = vf.createURI(CRUser.rootProjectUri()); URI userHomeUri = vf.createURI(user.getHomeUri()); URI reviewsUri = vf.createURI(user.getReviewsUri()); URI bookmarksUri = vf.createURI(user.getBookmarksUri()); URI registrationsUri = vf.createURI(user.getRegistrationsUri()); URI historyUri = vf.createURI(user.getHistoryUri()); URI rdfType = vf.createURI(Predicates.RDF_TYPE); URI rdfsLabel = vf.createURI(Predicates.RDFS_LABEL); URI allowSubObjectType = vf.createURI(Predicates.CR_ALLOW_SUBOBJECT_TYPE); URI hasFolder = vf.createURI(Predicates.CR_HAS_FOLDER); URI hasFile = vf.createURI(Predicates.CR_HAS_FILE); URI folder = vf.createURI(Subjects.CR_FOLDER); URI file = vf.createURI(Subjects.CR_FILE); URI userFolder = vf.createURI(Subjects.CR_USER_FOLDER); URI reviewFolder = vf.createURI(Subjects.CR_REVIEW_FOLDER); URI bookmarksFile = vf.createURI(Subjects.CR_BOOKMARKS_FILE); URI registrationsFile = vf.createURI(Subjects.CR_REGISTRATIONS_FILE); URI historyFile = vf.createURI(Subjects.CR_HISTORY_FILE); Literal homeLabel = vf.createLiteral(user.getUserName() + "'s home"); Literal reviewsLabel = vf.createLiteral(user.getUserName() + "'s reviews"); Literal bookmarksLabel = vf.createLiteral(user.getUserName() + "'s bookmarks"); Literal registrationsLabel = vf.createLiteral(user.getUserName() + "'s registrations"); Literal historyLabel = vf.createLiteral(user.getUserName() + "'s history"); ArrayList<Statement> result = new ArrayList<Statement>(); // statements about all user homes root (e.g. http://cr.eionet.europa.eu/home) result.add(new ContextStatementImpl(rootHomeUri, hasFolder, userHomeUri, rootHomeUri)); // statements about project root (e.g. http://cr.eionet.europa.eu/project) result.add(new ContextStatementImpl(rootProjectUri, rdfType, folder, rootProjectUri)); // statements about user home URI result.add(new ContextStatementImpl(userHomeUri, rdfType, userFolder, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, rdfsLabel, homeLabel, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, allowSubObjectType, folder, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, allowSubObjectType, file, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, hasFolder, reviewsUri, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, hasFile, bookmarksUri, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, hasFile, registrationsUri, userHomeUri)); result.add(new ContextStatementImpl(userHomeUri, hasFile, historyUri, userHomeUri)); // statements about reviews URI result.add(new ContextStatementImpl(reviewsUri, rdfType, reviewFolder, userHomeUri)); result.add(new ContextStatementImpl(reviewsUri, rdfsLabel, reviewsLabel, userHomeUri)); result.add(new ContextStatementImpl(reviewsUri, allowSubObjectType, folder, userHomeUri)); result.add(new ContextStatementImpl(reviewsUri, allowSubObjectType, file, userHomeUri)); // statements about bookmarks URI result.add(new ContextStatementImpl(bookmarksUri, rdfType, bookmarksFile, userHomeUri)); result.add(new ContextStatementImpl(bookmarksUri, rdfsLabel, bookmarksLabel, userHomeUri)); // statements about registrations URI result.add(new ContextStatementImpl(registrationsUri, rdfType, registrationsFile, userHomeUri)); result.add(new ContextStatementImpl(registrationsUri, rdfsLabel, registrationsLabel, userHomeUri)); // statements about history URI result.add(new ContextStatementImpl(historyUri, rdfType, historyFile, userHomeUri)); result.add(new ContextStatementImpl(historyUri, rdfsLabel, historyLabel, userHomeUri)); return result; } /** * @param sqlConn * @param statements * @throws SQLException */ private void createNeverHarvestedSources(Connection sqlConn, List<Statement> statements) throws SQLException { // Create harvest sources for all distinct contexts found in the above statements. // Doing it one-by-one for convenience, because the number distinct contexts in the // given statements should never be more than a couple or so. HashSet<String> sourcesDone = new HashSet<String>(); for (Statement statement : statements) { String sourceUrl = statement.getContext().stringValue(); if (!sourcesDone.contains(sourceUrl)) { List<Object> values = new ArrayList<Object>(); values.add(sourceUrl); values.add(Hashes.spoHash(sourceUrl)); SQLUtil.executeUpdate(INSERT_NEVER_HARVESTED_SOURCE_SQL, values, sqlConn); sourcesDone.add(sourceUrl); } } } public void createProjectBookmarksFolder(String projectName) throws DAOException { Connection sqlConn = null; RepositoryConnection repoConn = null; try { sqlConn = SesameUtil.getSQLConnection(); sqlConn.setAutoCommit(false); repoConn = SesameUtil.getRepositoryConnection(); repoConn.setAutoCommit(false); ValueFactory vf = repoConn.getValueFactory(); List<Statement> statements = getProjectFolderCreationStatements(projectName, vf); repoConn.add(statements); repoConn.commit(); sqlConn.commit(); } catch (OpenRDFException e) { SesameUtil.rollback(repoConn); throw new DAOException(e.getMessage(), e); } catch (SQLException e) { SQLUtil.rollback(sqlConn); throw new DAOException(e.getMessage(), e); } finally { SQLUtil.close(sqlConn); SesameUtil.close(repoConn); } } }