OverviewEnhance the Framework / Server / Service to support "MIME" Data. RESTful interfaceA configured relationship will be used to provide the URI interface
OpenPTK Configuration Elements for MediaContext
<Context id="Media-MySQL-JDBC" enabled="true" definition="Media" connection="MySQL" association="JDBC-MEDIA"> <Properties> <Property name="context.description" value="Media to MySQL using JDBC" /> <Property name="operation.classname" value="org.openptk.spi.operations.JdbcOperations" /> <Property name="connection.table" value="media" /> <Property name="key" value="uniqueid" /> <Property name="timeout" value="%{timeout.write}" /> <Property name="search.default.order" value="name,subject" /> <Property name="search.operators" value="AND,OR,CONTAINS,EQ" /> </Properties> <Query type="NULL" /> <Operations> <Operation id="create" attrgroup="media-create"> <Actions> <Action id="checkmimetype" mode="pre" > <Properties> <Property name="attribute.type" value="type"/> </Properties> </Action> <Action id="ifexists" mode="pre" /> <Action id="thumbnail" mode="post" /> </Actions> </Operation> <Operation id="read" attrgroup="media-read" /> <Operation id="update" attrgroup="media-update" > <Actions> <Action id="checkmimetype" mode="pre" > <Properties> <Property name="attribute.type" value="type"/> </Properties> </Action> <Action id="thumbnail" mode="post" /> </Actions> </Operation> <Operation id="delete" attrgroup="media-delete" /> <Operation id="search" attrgroup="media-search"> <Properties> <Property name="timeout" value="%{timeout.read}" /> <Property name="sort" value="name" /> </Properties> </Operation> </Operations> </Context> Definition
<Definition id="Media"> <Properties> <Property name="definition.classname" value="org.openptk.definition.BasicSubject" /> <Property name="definition.description" value="Media Repository" /> </Properties> <Attributes> <Attribute id="uniqueid" required="true"> </Attribute> <Attribute id="name" /> <Attribute id="type" required="true"> <Functions> <Function id="MimeType" classname="org.openptk.definition.functions.DetectMimeType"> <Arguments> <Argument name="data" type="attribute" value="data" /> <Argument name="plugin" type="literal" value="mimeutil"/> </Arguments> <Operations> <Operation type="create"/> <Operation type="update"/> </Operations> </Function> </Functions> </Attribute> <Attribute id="length" required="true" type="integer"/> <Attribute id="modified" required="true"> <Functions> <Function id="DateTime" classname="org.openptk.definition.functions.DateTimeStamp"> <Operations> <Operation type="create"/> <Operation type="update"/> </Operations> </Function> </Functions> </Attribute> <Attribute id="contextid" /> <Attribute id="subjectid" /> <Attribute id="relationshipid" /> <Attribute id="digest" required="true" > <Functions> <Function id="Digest" classname="org.openptk.definition.functions.CalculateDigest"> <Arguments> <Argument name="data" type="attribute" value="data" /> </Arguments> <Operations> <Operation type="create"/> <Operation type="update"/> </Operations> </Function> </Functions> </Attribute> <Attribute id="data" type="object" required="true" /> </Attributes> </Definition> Association
<Association id="JDBC-MEDIA"> <Attributes> <Attribute id="uniqueid" servicename="uuid" /> <Attribute id="name" /> <Attribute id="type" /> <Attribute id="length" servicename="size"/> <Attribute id="modified" /> <Attribute id="contextid" servicename="context"/> <Attribute id="subjectid" servicename="subject"/> <Attribute id="relationshipid" servicename="relationship"/> <Attribute id="digest" /> <Attribute id="data" /> </Attributes> </Association> AttrGroups
<AttrGroup id="media-create"> <Attributes> <Attribute id="name" /> <Attribute id="type" /> <Attribute id="length" /> <Attribute id="modified" /> <Attribute id="contextid" /> <Attribute id="subjectid" /> <Attribute id="relationshipid" /> <Attribute id="digest" /> <Attribute id="data" /> </Attributes> </AttrGroup> <AttrGroup id="media-read"> <Attributes> <Attribute id="name" /> <Attribute id="type" /> <Attribute id="length" /> <Attribute id="modified" /> <Attribute id="contextid" /> <Attribute id="subjectid" /> <Attribute id="relationshipid" /> <Attribute id="digest" /> <Attribute id="data" /> </Attributes> </AttrGroup> <AttrGroup id="media-update"> <Attributes> <Attribute id="name" /> <Attribute id="type" /> <Attribute id="length" /> <Attribute id="modified" /> <Attribute id="contextid" /> <Attribute id="subjectid" /> <Attribute id="relationshipid" /> <Attribute id="digest" /> <Attribute id="data" /> </Attributes> </AttrGroup> <AttrGroup id="media-delete"/> <AttrGroup id="media-search"> <Attributes> <Attribute id="name" /> <Attribute id="contextid" /> <Attribute id="subjectid" /> <Attribute id="relationshipid" /> </Attributes> </AttrGroup> Media DatabaseFirst Generation DesignThis is an initial DB design to support the relationship media. For the "My Photo" Use Case the uniqueid, in the Media Context, is derived by merging the contextId, SubjectId and relationshipId. The Relationship implementations would be able to derive the "primary key" (uniqueid) and directly check / create / read the related media. This design can be used as a general purpose "media storage mechanism". The business logic will need to generate a uniqueid using the GUID facility. Note: the "syntax" of the GUID (primary key) will not match the Relationship syntax. There's no reason why this design should not be able to support both Relationship media and General-Purpose media.
CREATE TABLE media ( uuid varchar(255) primary key not null, name varchar(255), type varchar(255), size int, modified varchar(255), digest varchar(255), data mediumblob not null, context varchar(255), subject varchar(255), relationship varchar(255), INDEX INDEX1 (context), INDEX INDEX2 (subject), INDEX INDEX3 (relationship) ); Second Generation DesignThis is a proposed DB design to better support both relationship and general media requirements. The First Generation was focused on the supporting the "photo" use case requirements. Since the Media Context can be used as a general purpose storage mechanism (for images, documents, etc.) it should to be enahanced. The First Generation assumes that the uniqueid of the Media Context is derived from the Relationship process. This Second Generation design is focused on enhancing the Media Context to support business Use Cases for storing media items that may not have a "relationship". Supporting Relationships: The Relationship mechanisms will need to change:
CREATE TABLE media ( id varchar(255) primary key not null, name varchar(255), type varchar(255), size int, modified varchar(255), digest varchar(255), data mediumblob not null, relid varchar(255), context varchar(255), subject varchar(255), relationship varchar(255), INDEX INDEX1 (relid), INDEX INDEX2 (context), INDEX INDEX3 (subject), INDEX INDEX4 (relationship) ); NotesThe max size of an image is associated to the datatype for MySQL
JDBC Sample codestoring the file in the table: File image = new File("C:/image.jpg"); fis = new FileInputStream(image); psmnt = connection.prepareStatement("insert into media(uuid, data) "+ "values(?,?)"); psmnt.setString(1,"1234-5678-9010"); psmnt.setBinaryStream(2, (InputStream)fis, (int)(image.length())); psmnt.executeUpdate(); Add byte array to JDBC database String sql = "INSERT INTO my_table (byte_array) VALUES (?)"; PreparedStatement statement = connection.prepareStatement(sql); byte[] buffer = somwhere.getSomeByteArray(); statement.setBytes(1, buffer); statement.executeUpdate(); statement.close(); Insert row into table using a statement import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class Main { public static void main(String[] argv) throws Exception { String driverName = "com.jnetdirect.jsql.JSQLDriver"; Class.forName(driverName); String serverName = "127.0.0.1"; String portNumber = "1433"; String mydatabase = serverName + ":" + portNumber; String url = "jdbc:JSQLConnect://" + mydatabase; String username = "username"; String password = "password"; Connection connection = DriverManager.getConnection(url, username, password); Statement stmt = connection.createStatement(); String sql = "INSERT INTO my_table (col_string) VALUES('a string')"; stmt.executeUpdate(sql); } } Insert rows into table using a prepared statement import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class Main { public static void main(String[] argv) throws Exception { String driverName = "com.jnetdirect.jsql.JSQLDriver"; Class.forName(driverName); String serverName = "127.0.0.1"; String portNumber = "1433"; String mydatabase = serverName + ":" + portNumber; String url = "jdbc:JSQLConnect://" + mydatabase; String username = "username"; String password = "password"; Connection connection = DriverManager.getConnection(url, username, password); String sql = "INSERT INTO my_table (col_string) VALUES(?)"; PreparedStatement pstmt = connection.prepareStatement(sql); // Insert 10 rows for (int i = 0; i < 10; i++) { // Set the value pstmt.setString(1, "row " + i); // Insert the row pstmt.executeUpdate(); } } } Client uploadingSimple HTML form POST <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head><title>File Upload To Database</title></head> <body> <h3>Please Choose a File and click Submit</h3> <form enctype="multipart/form-data" action="http://localhost:8080/openptk/resources/engine/contexts/Employees-MySQL-JDBC/subjects/aa10127/relationships/photo" method="post"> <input type="hidden" name="MAX_FILE_SIZE" value="10000000" /> <input type="file" name="media" /> <input type="submit" value="Submit" /> </form> </body> </html> It is important to use the enctype="multipart/form-data" so that the browser uploads the binary data correctly. File Upload Processing, Server SideThis section identifies the options for processing the file being uploaded, from the perspective of the Server. All of these options will use the JAX-RS / Jersey API for interfacing with the HTTP Methods GET, PUT, POST, DELETE. Apache Common, File Upload
package rest.resource; import java.io.File; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; @Path("fileupload") public class FileUploadResource { @POST @Produces("text/plain") public String loadFile(@Context HttpServletRequest request) { String resultStatus = "fail"; String fileRepository = "I:\\testRepo\\"; if (ServletFileUpload.isMultipartContent(request)) { FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items = null; try { items = upload.parseRequest(request); } catch (FileUploadException e) { e.printStackTrace(); } if (items != null) { Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); if (!item.isFormField() && item.getSize() > 0) { String fileName = processFileName(item.getName()); try { item.write(new File(fileRepository + fileName)); } catch (Exception e) { e.printStackTrace(); } resultStatus = "ok"; } } } } return resultStatus; } private String processFileName(String fileNameInput) { String fileNameOutput = null; fileNameOutput = fileNameInput.substring(fileNameInput.lastIndexOf("\\") + 1, fileNameInput.length()); return fileNameOutput; } } PUT with byte array@PUT public Response putItem( @Context HttpHeaders headers, byte[] data) { System.out.println("PUT ITEM " + container + " " + item); URI uri = uriInfo.getAbsolutePath(); MediaType mimeType = headers.getMediaType(); GregorianCalendar gc = new GregorianCalendar(); gc.set(GregorianCalendar.MILLISECOND, 0); Item i = new Item(item, uri.toString(), mimeType.toString(), gc); String digest = computeDigest(data); i.setDigest(digest); Response r; if (!MemoryStore.MS.hasItem(container, item)) { r = Response.created(uri).build(); } else { r = Response.noContent().build(); } Item ii = MemoryStore.MS.createOrUpdateItem(container, i, data); if (ii == null) { // Create the container if one has not been created URI containerUri = uriInfo.getAbsolutePathBuilder().path(".."). build().normalize(); Container c = new Container(container, containerUri.toString()); MemoryStore.MS.createContainer(c); i = MemoryStore.MS.createOrUpdateItem(container, i, data); if (i == null) throw new NotFoundException("Container not found"); } return r; } POST with Jersey FormParam InputStreamthe client is sending multipart/form-data. @Consumes("multipart/form-data") @POST public void post(@FormParam("file") InputStream file) { ... } POST with Jersey FormDataMultiPart
@Consumes("multipart/form-data") @POST public void post(FormDataMultiPart formData) { FormDataPart p = formData.getField("file"); InputStream file = p.getValueAs(InputStream.class); ... } InputStream to Byte ArrayInputStream in=xmlClob().getAsciiStream(); int c; while ((c = in.read()) != -1) { byteArrayOutputStream.write((char) c); } ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); import java.io.*; public class InputstramToByteArray { public static void main(String args[])throws IOException { InputStream itStrm=new FileInputStream( "InputstramToByteArray.java" ); String str=itStrm.toString(); byte[] b3=str.getBytes(); for(int i=0;i<b3.length;i++) System.out.print(b3[i]+"\t"); } } int bytesRead=0; int bytesToRead=1024; byte[] input = new byte[bytesToRead]; while (bytesRead < bytesToRead) { int result = in.read(input, bytesRead, bytesToRead - bytesRead); if (result == -1) break; bytesRead += result; } For either of the above you require a dependency on JavaMail: Checking the results of the prepared statement Connection conn = getConnection(); conn.setAutoCommit(false); Statement st = conn.createStatement(); st.executeUpdate("create table survey (id int, name VARCHAR(30) );"); String INSERT_RECORD = "insert into survey(id, name) values(?,?)"; PreparedStatement pstmt = conn.prepareStatement(INSERT_RECORD); pstmt.setInt(1, 1); pstmt.setString(2, "name"); pstmt.executeUpdate(); // Get warnings on PreparedStatement object SQLWarning warning = pstmt.getWarnings(); while (warning != null) { // Process statement warnings... String message = warning.getMessage(); String sqlState = warning.getSQLState(); int errorCode = warning.getErrorCode(); warning = warning.getNextWarning(); } OpenPTK-Plugin-MimeUtilA new OpenPTK project that provides an interface to the mime-util service. The mime-util service is used to determine the Mime-Type of a document.
This project will require these jar files:
Apache JackrabbitApache Jackrabbit is a open source Content Management System that implements the Java Specification Request (JSR 170) for the Java Content Repository (JCR 1.0) API. In order to use the "local" API of Jackrabbit/JCR it MUST be installed in the same web container as the application (OpenPTK). The application (OpenPTK) accesses the Jackrabbit/JCR API by obtaining its ServletContext, from the same web container. DownloadsGlassfish v3 Install
Reference |
Projects >