/******************************************************************************* * Copyright (c) 2012 IBM Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Keith Wells - initial API and implementation * Sam Padgett - initial API and Implementation * Jim Conallen - initial API and implementation * *******************************************************************************/ package org.eclipse.lyo.samples.sharepoint.store; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.lyo.samples.sharepoint.core.IConstants; import org.eclipse.lyo.samples.sharepoint.l10n.Messages; import org.eclipse.lyo.samples.sharepoint.store.ShareValue.ShareValueType; import org.openrdf.model.BNode; import org.openrdf.model.Literal; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.query.Binding; import org.openrdf.query.BindingSet; import org.openrdf.query.QueryLanguage; import org.openrdf.query.TupleQuery; import org.openrdf.query.TupleQueryResult; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import org.openrdf.repository.sail.SailRepository; import org.openrdf.rio.RDFFormat; import org.openrdf.sail.memory.MemoryStore; import org.openrdf.sail.nativerdf.NativeStore; public class ShareStore { static private File binResources = null; static private ShareStore _store = null; static public ShareStore initalizeStore(String repositoryLocation, String binaryResourceLocation, String host, String context) throws ShareServerException{ if( _store != null ) { shutdown(); } System.out.println( Messages.getString("ShareStore.InitializingStore")); //$NON-NLS-1$ _store = new ShareStore(); _store.host = host; _store.context = context; File dataDir = new File(repositoryLocation); _store.repository = new SailRepository(new NativeStore(dataDir)); try { _store.repository.initialize(); binResources = new File(binaryResourceLocation); if( !binResources.exists() ) { binResources.mkdir(); } } catch (Exception e) { throw new ShareServerException(e); } return _store; } public static boolean isStoreInitialized() { return (_store != null); } public static File getBinFolder(){ return binResources; } public static ShareStore getStore() throws ShareServerException { if( _store == null ) { throw new ShareServerException(Messages.getString("ShareStore.RDFStoreNotInitialized")); //$NON-NLS-1$ } return _store; } private ShareStore() { } private Repository repository = null; private String host = null; private String context = null; synchronized private Repository getRepository() throws ShareServerException { if( repository == null ) { throw new ShareServerException("RDF Store not initialized"); //$NON-NLS-1$ } return repository; } private Repository createMemoryRepository() throws ShareServerException { Repository repository = new SailRepository(new MemoryStore()); try { repository.initialize(); } catch (RepositoryException e) { throw new ShareServerException(e); } return repository; } private int openConnections = 0; private RepositoryConnection getConnection() throws ShareServerException { try { openConnections++; System.out.println( "Open: " + openConnections ); //$NON-NLS-1$ return getRepository().getConnection(); } catch (RepositoryException e) { throw new ShareServerException(e); } } private void close( RepositoryConnection conn ) { if( conn == null ) return; try { openConnections--; System.out.println( "Close: " + openConnections ); //$NON-NLS-1$ if( conn.isOpen() ) conn.close(); } catch (RepositoryException e) { e.printStackTrace(); } } synchronized public String nextAvailableUri(String type) throws ShareServerException { RepositoryConnection conn = getConnection(); try{ ValueFactory vf = getRepository().getValueFactory(); URI uriCounter = vf.createURI(IConstants.URI_COUNTER); URI uriServer = vf.createURI(IConstants.URI_SERVER); int counter = -1; RepositoryResult<Statement> statements = conn.getStatements(uriServer, uriCounter, null, false); // we expect at most 1 if( !statements.hasNext() ) { // create a new one counter = 1; Literal udpatedValue = vf.createLiteral(Integer.toString(counter+1), IConstants.XSD_DATATYPE_INT); // set the next value conn.add(uriServer, uriCounter, udpatedValue, (Resource) uriServer); } else { Statement statement = statements.next(); Value obj = statement.getObject(); if( obj instanceof Literal ) { counter = ((Literal)obj).intValue(); // now remove the old and add the new conn.remove(statement); // add add the updated one Literal udpatedValue = vf.createLiteral(counter+1); conn.add(uriServer, uriCounter, udpatedValue, (Resource) uriServer); } else { throw new ShareServerException(Messages.getString("ShareStore.UnableToGetUriCounter")); //$NON-NLS-1$ } } return getUriBase() + '/' + type + '/' + counter; } catch (RepositoryException e) { throw new ShareServerException(e); } finally { close(conn); } } public String getUriBase() { return host + '/' + context; } public String getServerContext() { return context; } /* * Resource */ public OslcResource getOslcResource(String uri) throws ShareServerException { OslcResource oslcResource = new OslcResource(uri); return (OslcResource) getResource(oslcResource); } public ShareResource getResource(String uri) throws ShareServerException { ShareResource resource = new ShareResource(uri); return getResource(resource); } public ShareResource getResource(ShareResource resource) throws ShareServerException { String uri = resource.getUri(); RepositoryConnection conn = getConnection(); ValueFactory vf = conn.getValueFactory(); URI resourceUri = vf.createURI(uri); // make sure there are some statements with this context RepositoryResult<Statement> statements = null; try { statements = conn.getStatements(null, null, null, false, (Resource) resourceUri); if( statements != null && statements.hasNext() ) { resource.statements.clear(); while( statements.hasNext() ) { Statement statement = statements.next(); String subject = statement.getSubject().stringValue(); String predicate = statement.getPredicate().stringValue(); ShareValue object = convertValue(statement.getObject()); ShareStatement rioStatement = new ShareStatement(subject, predicate, object, uri); resource.addStatement(rioStatement); } } else { resource = null; } } catch (Exception e) { throw new ShareServerException(e); } finally { close(conn); } return resource; } /** * @param resource * @throws ShareServerException */ public void update(ShareResource resource, String user) throws ShareServerException { RepositoryConnection conn = getConnection(); if( user == null ) { user = getDefaultUserUri(); } try { ShareResource oldResource = null; if( resource instanceof OslcResource ) { OslcResource oslcResource = new OslcResource(resource.getResourceContext()); oldResource = this.getResource(oslcResource); } else { oldResource = this.getResource(resource.getResourceContext()); } ShareValue propCreated = null; ShareValue propCreator = null; // if has a created or created property, we need to keep and ensure it is put back // these are system defined properties so we can do what we want if( oldResource != null ) { if( resource instanceof OslcResource ) { propCreated = oldResource.getFirstPropertyValue(IConstants.DCTERMS_CREATED); propCreator = oldResource.getFirstPropertyValue(IConstants.DCTERMS_CREATOR); if( propCreator != null ) { // then add it back resource.setUriProperty(IConstants.DCTERMS_CREATOR, propCreator.stringValue()); } if( propCreated != null ) { // add it back resource.setDateProperty(IConstants.DCTERMS_CREATED, propCreated.dateValue()); } } // remove the resource from the repo so we can update it. this.remove(resource); } // set the dc:identifier property to be the same as the last segment of this resource uri if( resource instanceof OslcResource ) { OslcResource oslcResource = (OslcResource) resource; String id = extractLastSegment(resource.getUri()); oslcResource.setIdentifier(id); oslcResource.setContributor(user); oslcResource.setModified(new Date()); if( propCreated != null ) { oslcResource.setCreated(propCreated.dateValue()); } else { oslcResource.setCreated(new Date()); } if( propCreator != null ) { oslcResource.setCreator(propCreator.stringValue()); } else { oslcResource.setCreator(user); } } ValueFactory vf = conn.getValueFactory(); URI resourceUri = vf.createURI(resource.getUri()); // explicitly using uri not resource context since this is a top level resource List<ShareStatement> rioStatements = resource.getStatements(); // make sure there are some statements with this context for (ShareStatement rioStatement : rioStatements) { URI pred = vf.createURI(rioStatement.getPredicate()); Resource subj = null; String rioSubject = rioStatement.getSubject(); if( rioStatement.isBNode() ) { subj = vf.createBNode(rioSubject); } else { subj = vf.createURI(rioSubject); } Value val = null; ShareValue rioVal = rioStatement.getObject(); if( rioVal.getType() == ShareValueType.URI ) { val = vf.createURI(rioVal.stringValue()); } else if( rioVal.getType() == ShareValueType.BLANK_NODE ) { val = vf.createBNode(rioVal.stringValue()); } else if( rioVal.getType() == ShareValueType.BOOLEAN ) { val = vf.createLiteral(rioVal.booleanValue()); } else if( rioVal.getType() == ShareValueType.CALENDAR ) { val = vf.createLiteral(rioVal.xmlGregorianCalendarValue()); } else if( rioVal.getType() == ShareValueType.DECIMAL ) { val = vf.createLiteral(rioVal.doubleValue()); } else if( rioVal.getType() == ShareValueType.INTEGER ) { val = vf.createLiteral(rioVal.intValue()); } else { val = vf.createLiteral(rioVal.stringValue()); } Statement statement = vf.createStatement(subj, pred, val); conn.add(statement, (Resource) resourceUri ); } } catch (Exception e) { throw new ShareServerException(e); } finally { close(conn); } } /** * @return */ public String getDefaultUserUri() { return this.getUriBase() + '/' + IConstants.SHARE_UNKNOWN_USER_ID; //$NON-NLS-1$ } public void storeBinaryResource(InputStream is, String id ) throws IOException { File file = new File(binResources, id); OutputStream out = new FileOutputStream(file); // Transfer bytes from in to out byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) > 0) { out.write(buf, 0, len); } is.close(); out.close(); } @SuppressWarnings({ }) static private Map<String,ShareValueType> rdfOriTypeMap = new HashMap<String,ShareValueType>(); static { rdfOriTypeMap.put(IConstants.XSD_DATATYPE_BOOLEAN, ShareValueType.BOOLEAN); rdfOriTypeMap.put(IConstants.XSD_DATATYPE_DECIMAL, ShareValueType.DECIMAL); rdfOriTypeMap.put(IConstants.XSD_DATATYPE_INT, ShareValueType.INTEGER); rdfOriTypeMap.put(IConstants.XSD_DATATYPE_DATETIME, ShareValueType.CALENDAR); rdfOriTypeMap.put("http://www.w3.org/2001/XMLSchema#dateTime", ShareValueType.CALENDAR); //$NON-NLS-1$ } static public ShareValue convertValue(org.openrdf.model.Value rdfValue) throws UnrecognizedValueTypeException { if( rdfValue instanceof Literal ) { Literal literal = (Literal) rdfValue; URI datatype = literal.getDatatype(); if( datatype != null ) { ShareValueType type = rdfOriTypeMap.get(datatype.stringValue()); if( type != null ) { switch( type ) { case BOOLEAN: return new ShareValue(type, literal.booleanValue()); case DECIMAL: return new ShareValue(type, literal.decimalValue()); case CALENDAR: return new ShareValue(type, literal.calendarValue()); case INTEGER: return new ShareValue(type, literal.integerValue()); default: return new ShareValue(type, literal.toString() ); } } } return new ShareValue(ShareValueType.STRING, rdfValue.stringValue()); // default to this type } else { // expect a uri or blank node // todo: might want to make uri and blank node different types if( rdfValue instanceof BNode ) { return new ShareValue(ShareValueType.BLANK_NODE, java.net.URI.create(rdfValue.stringValue())); } if( rdfValue.stringValue().startsWith("<") ) { System.out.println(rdfValue.stringValue()); } return new ShareValue(ShareValueType.URI, java.net.URI.create(rdfValue.stringValue())); } } static public org.openrdf.model.Value toShareValue(ValueFactory vf, ShareValue rioValue) throws UnrecognizedValueTypeException, IncompatibleValueException { switch( rioValue.getType() ) { case BOOLEAN: { Literal val = vf.createLiteral(rioValue.booleanValue()); return val; } case INTEGER: { Literal val = vf.createLiteral(rioValue.intValue()); return val; } case CALENDAR: { Literal val = vf.createLiteral(rioValue.xmlGregorianCalendarValue()); return val; } case STRING: { Literal val = vf.createLiteral(rioValue.stringValue()); return val; } case URI: { URI uri = vf.createURI(rioValue.stringValue()); return uri; } case BLANK_NODE: { BNode bnode = vf.createBNode(rioValue.stringValue()); return bnode; } default: { throw new UnrecognizedValueTypeException(); } } } public List<ShareStatement> parse(String resUri, InputStream is, String contentType) throws ShareServerException { String format = rdfFormatFromContentType(contentType); if( format == null ) { throw new ShareServerException(Messages.getString("ShareStore.UnrecognizedContentType")); //$NON-NLS-1$ } Repository memRepo = createMemoryRepository(); if( resUri == null ) { throw new ShareServerException("Resource URI required"); } ValueFactory vf = getRepository().getValueFactory(); URI uri = vf.createURI(resUri); RepositoryConnection memConn = null; try { memConn = memRepo.getConnection(); RDFFormat rdfFormat = RDFFormat.valueOf(format); memConn.add(is, resUri, rdfFormat, (Resource) uri); // build the resource return List<ShareStatement> rioStatements = new ArrayList<ShareStatement>(); RepositoryResult<Statement> statements = memConn.getStatements(null, null, null, false, (Resource) uri); while( statements.hasNext()) { Statement statement = statements.next(); String subject = statement.getSubject().stringValue(); String predicate = statement.getPredicate().stringValue(); ShareValue object = convertValue(statement.getObject()); ShareStatement rioStatement = new ShareStatement(subject, predicate, object, resUri); rioStatements.add(rioStatement); } return rioStatements; } catch (Exception e) { throw new ShareServerException(e); } finally { try { memConn.close(); } catch (RepositoryException e) { e.printStackTrace(); } } } public List<ShareStatement> findStatements(String subject, String predicate, ShareValue value, String resource ) throws ShareServerException, UnrecognizedValueTypeException { List<ShareStatement> statements = new ArrayList<ShareStatement>(); RepositoryConnection conn = this.getConnection(); ValueFactory vf = conn.getValueFactory(); Resource sub = vf.createURI(subject); URI pred = vf.createURI(predicate); RepositoryResult<Statement> results = null; try{ if( resource != null ) { URI res = vf.createURI(resource); results = conn.getStatements(sub, pred, null, false, (Resource) res); } else { results = conn.getStatements(sub, pred, null, false); } while( results.hasNext() ) { Statement result = results.next(); ShareStatement rioStatement = toShareStatement(result); statements.add(rioStatement); } return statements; } catch( RepositoryException re ) { throw new ShareServerException(re); } } static public ShareStatement toShareStatement(Statement statement) throws UnrecognizedValueTypeException { String subject = statement.getSubject().stringValue(); String predicate = statement.getPredicate().stringValue(); Value obj = statement.getObject(); ShareValue rioVal = convertValue(obj); String context = statement.getContext().stringValue(); ShareStatement rioStatement = new ShareStatement(subject, predicate, rioVal, context); return rioStatement; } /* * Query */ public List<Map<String,ShareValue>> query(String queryLanguage, String query, int maxResults) throws ShareServerException { return query(null, queryLanguage, query, maxResults); } private List<Map<String,ShareValue>> query(Repository repository, String queryLanguage, String query, int maxResults) throws ShareServerException { if( maxResults <= 0 ) maxResults = DEFAULT_MAX_RESULTS; QueryLanguage language = QueryLanguage.valueOf(queryLanguage); if( language == null ) { throw new ShareServerException(Messages.getString("ShareStore.UnrecognizedQueryLanguage") + queryLanguage ); //$NON-NLS-1$ } RepositoryConnection conn = null; List<Map<String,ShareValue>> bindings = new ArrayList<Map<String,ShareValue>>(); try { if( repository == null ) { conn = getConnection(); } else { conn = repository.getConnection(); } TupleQuery tupleQuery = conn.prepareTupleQuery(language, query); TupleQueryResult result = tupleQuery.evaluate(); int count = 0; while( result.hasNext() && count<maxResults) { BindingSet bindingSet = result.next(); HashMap<String, ShareValue> map = new HashMap<String,ShareValue>(); Set<String> names = bindingSet.getBindingNames(); for (String name : names) { Binding binding = bindingSet.getBinding(name); Value value = binding.getValue(); ShareValue rioValue = convertValue(value); map.put(name, rioValue); } bindings.add(map); } } catch (Exception e) { throw new ShareServerException(e); } finally { close(conn); } return bindings; } /** * @param resource * @throws ShareServerException */ public void remove(ShareResource resource) throws ShareServerException { RepositoryConnection conn = null; try { conn = getConnection(); ValueFactory vf = conn.getValueFactory(); Resource res = vf.createURI(resource.getUri()); conn.remove((URI) null, null, null, (Resource) res); } catch (Exception e) { throw new ShareServerException(e); } finally { close(conn); } } /** * @param id * @return * @throws FileNotFoundException */ public InputStream getBinaryResource(String id) throws FileNotFoundException { File file = new File(binResources, id); FileInputStream is = new FileInputStream(file); return is; } public static void shutdown() throws ShareServerException { if( ShareStore._store == null ) return; Repository repo = getStore().getRepository(); try { repo.shutDown(); ShareStore._store = null; } catch (RepositoryException e) { throw new ShareServerException(e); } } static public void dump(RepositoryConnection conn) { System.out.println( "Dumping Repository: "); //$NON-NLS-1$ try{ if( conn == null ) { conn = getStore().getConnection(); } RepositoryResult<Statement> allStatements = conn.getStatements(null, null, null, false); while( allStatements.hasNext() ) { Statement statement = allStatements.next(); System.out.println( statement.toString() ); } allStatements.close(); } catch( Exception e ) { e.printStackTrace(); } finally { try{ if( conn != null && conn.isOpen() ) conn.close(); } catch( Exception e ) { e.printStackTrace(); // log this? } } } static public void dump( Repository repository ) throws RepositoryException{ RepositoryConnection conn = repository.getConnection(); dump( conn ); } static public void dump(RepositoryConnection conn, Writer writer) { try{ writer.write( "Dumping Repository: \n" ); //$NON-NLS-1$ if( conn == null ) { conn = getStore().getConnection(); } RepositoryResult<Resource> contextIds = conn.getContextIDs(); while( contextIds.hasNext() ) { Resource contextId = contextIds.next(); writer.write( '\n' + Messages.getString("ShareStore.Resource") + contextId.toString() + '\n'); //$NON-NLS-1$ RepositoryResult<Statement> statements = conn.getStatements(null, null, null, false, contextId); while( statements.hasNext() ) { Statement statement = statements.next(); Resource sub = statement.getSubject(); URI pred = statement.getPredicate(); Value obj = statement.getObject(); if( obj instanceof URI ) { writer.write( "\t<" + sub.toString() + "> <" + pred.toString() + "> <" + obj.toString() + ">\n" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } else { writer.write( "\t<" + sub.toString() + "> <" + pred.toString() + "> " + obj.toString() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } statements.close(); } contextIds.close(); } catch( Exception e ) { e.printStackTrace(); } finally { try{ if( conn != null && conn.isOpen() ) conn.close(); } catch( Exception e ) { e.printStackTrace(); // log this? } } } public List<String> getResourceContexts(){ List<String> resourceIds = new ArrayList<String>(); RepositoryConnection conn = null; System.out.println( "Clearing Repository: "); //$NON-NLS-1$ try{ conn = getConnection(); RepositoryResult<Resource> contextIds = conn.getContextIDs(); while( contextIds.hasNext() ) { resourceIds.add( contextIds.next().stringValue() ); } } catch( Exception e ) { e.printStackTrace(); } finally { close(conn); } return resourceIds; } public void clear(String contextUri) { RepositoryConnection conn = null; System.out.println( "Clearing Repository: "); //$NON-NLS-1$ try{ conn = getConnection(); ValueFactory vf = conn.getValueFactory(); URI context = vf.createURI(contextUri); if( context != null ) { conn.clear(context); } } catch( Exception e ) { e.printStackTrace(); } finally { close(conn); } } public void clear(Resource context) { RepositoryConnection conn = null; System.out.println( "Clearing Repository: "); //$NON-NLS-1$ try{ conn = getConnection(); if( context == null ) { conn.clear(); } else { conn.clear(context); } } catch( Exception e ) { e.printStackTrace(); } finally { close(conn); } } public static String rdfFormatFromContentType( String contentType ) { int pos = contentType.indexOf(';'); if( pos > 0 ) { contentType = contentType.substring(0,pos); } if( IConstants.CT_RDF_XML.equals(contentType) ){ return RDFFormat.RDFXML.getName(); } else if( IConstants.CT_APP_N3.equals(contentType) || IConstants.CT_TEXT_N3.equals(contentType) ){ return RDFFormat.N3.getName(); } else if( IConstants.CT_APP_N_TRIPLES.equals(contentType) ){ return RDFFormat.NTRIPLES.getName(); } else if( IConstants.CT_TEXT_TURTLE.equals(contentType) || IConstants.CT_X_TURTLE.equals(contentType) ){ return RDFFormat.TURTLE.getName(); } return null; } static final int DEFAULT_MAX_RESULTS = 500; static private List<String> serverManagedProperties = null; static { serverManagedProperties = new ArrayList<String>(); serverManagedProperties.add(IConstants.OSLC_SERVICEPROVIDER); serverManagedProperties.add(IConstants.OSLC_INSTANCESHAPE); serverManagedProperties.add(IConstants.DCTERMS_IDENTIFIER); serverManagedProperties.add(IConstants.DCTERMS_CREATED); serverManagedProperties.add(IConstants.DCTERMS_CREATOR); serverManagedProperties.add(IConstants.DCTERMS_MODIFIED); serverManagedProperties.add(IConstants.DCTERMS_CONTRIBUTOR); } static public boolean isServerManaged(URI uri) { return serverManagedProperties.contains(uri.stringValue()); } static private Map<String,String> predefinedPrefixes = new HashMap<String,String>(); static { predefinedPrefixes.put(IConstants.RDF_NAMESPACE, IConstants.RDF_PREFIX); predefinedPrefixes.put(IConstants.DCTERMS_NAMESPACE, IConstants.DCTERMS_PREFIX); predefinedPrefixes.put(IConstants.OSLC_NAMESPACE, IConstants.OSLC_PREFIX); } static public Map<String, String> getPredefinedNamespaceMappings(){ return predefinedPrefixes; } @SuppressWarnings("nls") static public String sparqlDefaultPrefixes(){ StringBuilder prefixes = new StringBuilder(); Set<String> namespaces = predefinedPrefixes.keySet(); for (String namespace : namespaces) { String prefix = predefinedPrefixes.get(namespace); prefixes.append("PREFIX " + prefix + ":<" + namespace + ">\n" ); } return prefixes.toString(); } public static String extractLastSegment(String uri) { int pos = uri.lastIndexOf('#'); if( pos > 0 ) { return uri.substring(pos + 1); } pos = uri.lastIndexOf('/'); return uri.substring(pos + 1); } }