Add some metadata services
diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceContext.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceContext.java
index d9347c5..b94173c 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceContext.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceContext.java
@@ -17,6 +17,7 @@
 

 import java.io.IOException;

 import java.io.InputStream;

+import java.net.URI;

 import java.net.URL;

 import java.util.ArrayList;

 import java.util.HashMap;

@@ -79,6 +80,8 @@
     private EntityManagerFactory emf;

     

     private JAXBContext context = null;

+    

+    private URI baseURI = null;

 

     public PersistenceContext(Archive archive, Map<String, Object> properties, ClassLoader classLoader){

         super();

@@ -154,6 +157,14 @@
         return context;

     }

 

+    public URI getBaseURI() {

+        return baseURI;

+    }

+

+    public void setBaseURI(URI baseURI) {

+        this.baseURI = baseURI;

+    }

+

     public void create(String tenantId, DynamicEntity entity) {

         EntityManager em = getEmf().createEntityManager();

 

diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceFactory.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceFactory.java
index 430bca0..839c961 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceFactory.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/PersistenceFactory.java
@@ -19,6 +19,7 @@
 import java.util.HashMap;

 import java.util.List;

 import java.util.Map;

+import java.util.Set;

 

 import javax.ejb.Singleton;

 

@@ -86,7 +87,6 @@
     

     

     public PersistenceContext bootstrapPersistenceContext(String name, Archive archive, String persistenceXMLLocation, Map<String, ?> originalProperties, boolean replace){

-        System.out.println("--- bootstrap persitence context " + name + " - " + persistenceXMLLocation);

         initialize();

         PersistenceContext persistenceContext = getPersistenceContext(name);

         if (persistenceContext == null || replace){

@@ -110,6 +110,10 @@
     	return persistenceContexts.get(name);

     }

     

+    public Set<String> getPersistenceContextNames(){

+        return persistenceContexts.keySet();

+    }

+    

     public void closePersistenceContext(String name){

         PersistenceContext context = persistenceContexts.get(name);

         if (context != null){

diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/Service.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/Service.java
index d5aead0..309cb8a 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/Service.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/Service.java
@@ -16,11 +16,14 @@
 import static org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller.mediaType;

 

 import java.io.InputStream;

+import java.net.URI;

 import java.net.URL;

 import java.util.HashMap;

+import java.util.Iterator;

 import java.util.List;

 import java.util.Map;

 import java.util.Map.Entry;

+import java.util.Set;

 import java.util.logging.Logger;

 

 import javax.annotation.PreDestroy;

@@ -30,6 +33,7 @@
 import javax.ws.rs.GET;

 import javax.ws.rs.POST;

 import javax.ws.rs.PUT;

+import javax.ws.rs.DELETE;

 import javax.ws.rs.Path;

 import javax.ws.rs.PathParam;

 import javax.ws.rs.Produces;

@@ -42,6 +46,7 @@
 import javax.ws.rs.core.Response.ResponseBuilder;

 import javax.ws.rs.core.Response.Status;

 import javax.ws.rs.core.StreamingOutput;

+import javax.ws.rs.core.UriBuilder;

 import javax.ws.rs.core.UriInfo;

 import javax.xml.bind.JAXBElement;

 import javax.xml.bind.JAXBException;

@@ -49,7 +54,9 @@
 import javax.xml.transform.stream.StreamSource;

 

 import org.eclipse.persistence.config.PersistenceUnitProperties;

+import org.eclipse.persistence.descriptors.ClassDescriptor;

 import org.eclipse.persistence.dynamic.DynamicEntity;

+import org.eclipse.persistence.jpa.JpaHelper;

 import org.eclipse.persistence.jpa.rs.metadata.DatabaseMetadataStore;

 import org.eclipse.persistence.jpa.rs.util.IdHelper;

 import org.eclipse.persistence.jpa.rs.util.LinkAdapter;

@@ -64,8 +71,8 @@
  * @since EclipseLink 2.4.0

  */

 @Singleton

-@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

 @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

+@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

 @Path("/")

 public class Service {

 	static final Logger logger = Logger.getLogger("AppService");	

@@ -81,8 +88,9 @@
         this.factory = factory;

     }

     

-   @POST

+   @PUT

    @Path("/")

+   @Consumes({ MediaType.WILDCARD})

    public Response start(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in){

        ResponseBuilder rb = new ResponseBuilderImpl();

        try{

@@ -101,9 +109,30 @@
        return rb.build();

    }

    

-    @POST

+   @GET

+   @Path("/")

+   @Consumes({ MediaType.WILDCARD})

+   public Response getContexts(@Context HttpHeaders hh) {

+       ResponseBuilder rb = new ResponseBuilderImpl();

+       Set<String> contexts = factory.getPersistenceContextNames();

+       StringBuffer buffer = new StringBuffer();

+       buffer.append("[");

+       Iterator<String> contextIterator = contexts.iterator();

+       while (contextIterator.hasNext()){

+           buffer.append("\"" + contextIterator.next() + "\"");

+           if (contextIterator.hasNext()){

+               buffer.append(", ");

+           }

+       }           

+       buffer.append("]");

+       rb.status(Status.OK);    

+       rb.entity(new StreamingOutputMarshaller(null, buffer.toString(), hh.getAcceptableMediaTypes()));

+       return rb.build();

+   }

+   

+    @PUT

     @Path("{context}")

-    public Response bootstrap(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in){

+    public Response bootstrap(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, @Context UriInfo uriInfo, InputStream in){

         ResponseBuilder rb = new ResponseBuilderImpl();

         String urlString = getURL(hh);

         PersistenceContext persistenceContext = null;

@@ -120,11 +149,50 @@
                 persistenceContext = factory.bootstrapPersistenceContext(persistenceUnit, in, new HashMap<String, Object>(), replace);

             }

        } catch (Exception e){

+            e.printStackTrace();

             rb.status(Status.NOT_FOUND);

         }

         if (persistenceContext != null){

+            persistenceContext.setBaseURI(uriInfo.getBaseUri());

             rb.status(Status.CREATED);

         }

+

+        return rb.build();

+    }

+    

+    @GET

+    @Path("{context}")

+    @Consumes({ MediaType.WILDCARD})

+    public Response getTypes(@PathParam("context") String persistenceUnit, @Context HttpHeaders hh) {

+        ResponseBuilder rb = new ResponseBuilderImpl();

+        PersistenceContext app = get(persistenceUnit);

+        if (app == null){

+            rb.status(Status.NOT_FOUND);

+        } else {

+            Map<Class, ClassDescriptor> descriptors = JpaHelper.getServerSession(app.getEmf()).getDescriptors();

+            StringBuffer buffer = new StringBuffer();

+            

+            buffer.append("[");

+            Iterator<Class> contextIterator = descriptors.keySet().iterator();

+            while (contextIterator.hasNext()){

+                buffer.append("\"" + descriptors.get(contextIterator.next()).getAlias() + "\"");

+                if (contextIterator.hasNext()){

+                    buffer.append(", ");

+                }

+            }           

+            buffer.append("]");

+            rb.status(Status.OK);

+            rb.entity(new StreamingOutputMarshaller(null , buffer.toString(), hh.getAcceptableMediaTypes()));

+        }

+        return rb.build();

+    }

+    

+    @DELETE

+    @Path("{context}")

+    public Response removeContext(@PathParam("context") String persistenceUnit, @PathParam("type") String type, @Context HttpHeaders hh, InputStream in){

+        ResponseBuilder rb = new ResponseBuilderImpl();

+        factory.closePersistenceContext(persistenceUnit);

+        rb.status(Status.OK);

         return rb.build();

     }

     

@@ -234,7 +302,7 @@
         try {

             unmarshaller = app.getJAXBContext().createUnmarshaller();

             unmarshaller.setProperty(MEDIA_TYPE, acceptedMedia.toString());

-            unmarshaller.setAdapter(new LinkAdapter("http://localhost:8080/JPA-RS/auction/entity/", app));

+            unmarshaller.setAdapter(new LinkAdapter(app.getBaseURI().toString(), app));

             JAXBElement<?> element = unmarshaller.unmarshal(new StreamSource(in), app.getClass(type));

             return (DynamicEntity) element.getValue();

         } catch (JAXBException e) {

diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/LinkAdapter.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/LinkAdapter.java
index c11d6d1..a8e7a96 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/LinkAdapter.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/LinkAdapter.java
@@ -34,7 +34,7 @@
 

 public class LinkAdapter extends XmlAdapter<String, Object> {

 

-    private String baseURI = "http://example.com/DEFAULT/";

+    private String baseURI = null;

     protected PersistenceContext context;

 

     public LinkAdapter() {

@@ -44,7 +44,7 @@
         this.baseURI = baseURI;

         this.context = context;

     }

-

+    

     @Override

     @SuppressWarnings("rawtypes")

     // TODO Composite keys

@@ -53,7 +53,7 @@
             return null;

         }

         int lastSlash = v.lastIndexOf('/');

-        String entityType = v.substring(baseURI.length(), lastSlash);

+        String entityType = v.substring((baseURI + context.getName() + "/" ).length(), lastSlash);

         String entityId = v.substring(lastSlash + 1);

         ClassDescriptor descriptor = context.getDescriptor(entityType);

         DatabaseMapping idMapping = getIdMapping(descriptor);

@@ -102,7 +102,7 @@
             return "";

         }

         Object id = de.get(idMapping.getAttributeName());

-        String href = baseURI + v.getClass().getSimpleName() + "/"

+        String href = baseURI + context.getName() + "/"  + v.getClass().getSimpleName() + "/"

                 + id;

         return href;

     }

diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/StreamingOutputMarshaller.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/StreamingOutputMarshaller.java
index 5cf649a..4f6827f 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/StreamingOutputMarshaller.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/util/StreamingOutputMarshaller.java
@@ -16,12 +16,12 @@
 import static org.eclipse.persistence.jaxb.JAXBContext.MEDIA_TYPE;

 

 import java.beans.PropertyChangeListener;

+import java.io.BufferedOutputStream;

 import java.io.ByteArrayOutputStream;

 import java.io.IOException;

 import java.io.ObjectOutputStream;

 import java.io.OutputStream;

 import java.io.OutputStreamWriter;

-import java.io.StringWriter;

 import java.util.Collection;

 import java.util.Iterator;

 import java.util.List;

@@ -63,22 +63,9 @@
     }

 

     public void write(OutputStream output) throws IOException, WebApplicationException {

-        if (this.context.getJAXBContext() != null && this.result != null && !this.mediaType.equals(MediaType.WILDCARD_TYPE)) {

+        if (this.context != null && this.context.getJAXBContext() != null && this.result != null && !this.mediaType.equals(MediaType.WILDCARD_TYPE)) {

             try {

-                Marshaller marshaller = this.context.getJAXBContext().createMarshaller();

-                marshaller.setProperty(MEDIA_TYPE, this.mediaType.toString());

-                marshaller.setProperty(org.eclipse.persistence.jaxb.JAXBContext.INCLUDE_ROOT, false);

-                marshaller.setAdapter(new LinkAdapter("http://localhost:8080/JPA-RS/auction/entity/", context));

-                marshaller.setListener(new Marshaller.Listener() {

-                    @Override

-                    public void beforeMarshal(Object source) {

-                        DynamicEntityImpl sourceImpl = (DynamicEntityImpl)source;

-                        PropertyChangeListener listener = sourceImpl._persistence_getPropertyChangeListener();

-                        sourceImpl._persistence_setPropertyChangeListener(null);

-                        ((DynamicEntity)source).set("self", source);

-                        sourceImpl._persistence_setPropertyChangeListener(listener);

-                    }

-                });

+                Marshaller marshaller = createMarshaller(context, mediaType);

                 if (result instanceof Collection) {

                     @SuppressWarnings("unchecked")

                     Collection<Object> objs = (Collection<Object>) result;

@@ -117,6 +104,11 @@
             }

         } else if (result instanceof byte[]){

             output.write((byte[])result);

+        } else if (result instanceof String){

+            OutputStreamWriter writer = new OutputStreamWriter(output);

+            writer.write((String)result);

+            writer.flush();

+            writer.close();

         } else {

             ByteArrayOutputStream baos = new ByteArrayOutputStream();

             ObjectOutputStream oos = new ObjectOutputStream(baos);

@@ -126,7 +118,7 @@
             output.write(baos.toByteArray());

         }

     }

-

+    

     /**

      * Identify the preferred {@link MediaType} from the list provided. This

      * will check for JSON string or {@link MediaType} first then XML.

@@ -160,4 +152,22 @@
         }

         return false;

     }

+    

+    public static Marshaller createMarshaller(PersistenceContext context, MediaType mediaType) throws JAXBException{

+        Marshaller marshaller = context.getJAXBContext().createMarshaller();

+        marshaller.setProperty(MEDIA_TYPE, mediaType.toString());

+        marshaller.setProperty(org.eclipse.persistence.jaxb.JAXBContext.INCLUDE_ROOT, false);

+        marshaller.setAdapter(new LinkAdapter(context.getBaseURI().toString(), context));

+        marshaller.setListener(new Marshaller.Listener() {

+            @Override

+            public void beforeMarshal(Object source) {

+                DynamicEntityImpl sourceImpl = (DynamicEntityImpl)source;

+                PropertyChangeListener listener = sourceImpl._persistence_getPropertyChangeListener();

+                sourceImpl._persistence_setPropertyChangeListener(null);

+                ((DynamicEntity)source).set("self", source);

+                sourceImpl._persistence_setPropertyChangeListener(listener);

+            }

+        });

+        return marshaller;

+    }

 }

diff --git a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/websockets/JPARSWebSocket.java b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/websockets/JPARSWebSocket.java
index 601aaf8..6a865c6 100644
--- a/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/websockets/JPARSWebSocket.java
+++ b/JPA-RS Incubator/JPA-RS/src/org/eclipse/persistence/jpa/rs/websockets/JPARSWebSocket.java
@@ -28,6 +28,7 @@
 

 import org.eclipse.persistence.jpa.rs.PersistenceContext;

 import org.eclipse.persistence.jpa.rs.util.LinkAdapter;

+import org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller;

 

 import com.sun.grizzly.websockets.DataFrame;

 import com.sun.grizzly.websockets.DefaultWebSocket;

@@ -114,21 +115,7 @@
 

 	protected String marshallEntity(PersistenceContext context, Object entity) {

 		try {

-			JAXBContext jaxbContext = context.getJAXBContext();

-			Marshaller marshaller = jaxbContext.createMarshaller();

-			marshaller.setProperty(MEDIA_TYPE, MediaType.APPLICATION_JSON);

-			marshaller.setProperty(org.eclipse.persistence.jaxb.JAXBContext.INCLUDE_ROOT, false);

-            marshaller.setAdapter(new LinkAdapter("http://localhost:8080/JPA-RS/auction/entity/", context));

-            marshaller.setListener(new Marshaller.Listener() {

-                @Override

-                public void beforeMarshal(Object source) {

-                    DynamicEntityImpl sourceImpl = (DynamicEntityImpl)source;

-                    PropertyChangeListener listener = sourceImpl._persistence_getPropertyChangeListener();

-                    sourceImpl._persistence_setPropertyChangeListener(null);

-                    ((DynamicEntity)source).set("self", source);

-                    sourceImpl._persistence_setPropertyChangeListener(listener);

-                }

-            });

+            Marshaller marshaller = StreamingOutputMarshaller.createMarshaller(context, MediaType.APPLICATION_JSON_TYPE);

 			StringWriter stringWriter = new StringWriter();

 			marshaller.marshal(entity, stringWriter);

 

diff --git a/JPA-RS Incubator/tests/JPA-RS Tests/src/jpars/test/service/TestService.java b/JPA-RS Incubator/tests/JPA-RS Tests/src/jpars/test/service/TestService.java
index 6d615df..cfde8e6 100644
--- a/JPA-RS Incubator/tests/JPA-RS Tests/src/jpars/test/service/TestService.java
+++ b/JPA-RS Incubator/tests/JPA-RS Tests/src/jpars/test/service/TestService.java
@@ -21,6 +21,7 @@
 import java.io.IOException;

 import java.io.InputStream;

 import java.io.StringWriter;

+import java.net.URI;

 import java.net.URL;

 import java.util.ArrayList;

 import java.util.HashMap;

@@ -53,6 +54,7 @@
 import org.eclipse.persistence.jpa.rs.Service;

 import org.eclipse.persistence.jpa.rs.metadata.DatabaseMetadataStore;

 import org.eclipse.persistence.jpa.rs.util.LinkAdapter;

+import org.eclipse.persistence.jpa.rs.util.StreamingOutputMarshaller;

 import org.eclipse.persistence.sessions.server.ServerSession;

 import org.junit.AfterClass;

 import org.junit.BeforeClass;

@@ -81,9 +83,12 @@
             factory.setMetadataStore(new DatabaseMetadataStore());

             factory.getMetadataStore().setProperties(properties);

             factory.getMetadataStore().clearMetadata();

-            factory.bootstrapPersistenceContext("auction", new URL("file:///C:/EclipseLinkView2/incubator/JPA-RS Incubator/tests/JPA-RS Tests/src/xmldocs/auction-persistence.xml"), properties, true);

-            factory.bootstrapPersistenceContext("phonebook", new URL("file:///C:/EclipseLinkView2/incubator/JPA-RS Incubator/tests/JPA-RS Tests/src/xmldocs/phonebook-persistence.xml"), properties, true);

-

+            PersistenceContext context = factory.bootstrapPersistenceContext("auction", new URL("file:///C:/EclipseLinkView2/incubator/JPA-RS Incubator/tests/JPA-RS Tests/src/xmldocs/auction-persistence.xml"), properties, true);

+            context.setBaseURI(new URI("http://localhost:8080/JPA-RS/"));

+            

+            context = factory.bootstrapPersistenceContext("phonebook", new URL("file:///C:/EclipseLinkView2/incubator/JPA-RS Incubator/tests/JPA-RS Tests/src/xmldocs/phonebook-persistence.xml"), properties, true);

+            context.setBaseURI(new URI("http://localhost:8080/JPA-RS/"));

+            

             clearData();

         } catch (Exception e){

             fail(e.toString());

@@ -226,6 +231,8 @@
             factory.setMetadataStore(new DatabaseMetadataStore());

             factory.getMetadataStore().setProperties(properties);

             factory.initialize(properties);

+            factory.getPersistenceContext("auction").setBaseURI(new URI("http://localhost:8080/JPA-RS/"));

+            factory.getPersistenceContext("phonebook").setBaseURI(new URI("http://localhost:8080/JPA-RS/"));

         } catch (Exception e){

             fail(e.toString());

         }

@@ -354,12 +361,30 @@
         assertTrue("Laptop was not in results.", resultString.contains("\"description\" : \"Speedy\""));

     }

     

+    @Test 

+    public void testMetadataQuery(){

+        Service service = new Service();

+        service.setPersistenceFactory(factory);

+        StreamingOutput output = (StreamingOutput)service.getContexts(generateHTTPHeader(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON)).getEntity();

+        String result = stringifyResults(output);

+        assertTrue("auction was not in the results", result.contains("auction"));

+        assertTrue("phonebook was not in the results", result.contains("phonebook"));

+        

+        output = (StreamingOutput)service.getTypes("auction", generateHTTPHeader(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON)).getEntity();

+        result = stringifyResults(output);

+        

+        assertTrue("Bid was not in the results", result.contains("Bid"));

+        assertTrue("Auction was not in the results", result.contains("Auction"));

+        assertTrue("User was not in the results", result.contains("User"));

+

+    }

+    

     private static DynamicEntity unmarshalEntity(PersistenceContext app, String type, String tenantId, String acceptedMedia, InputStream in) {

         Unmarshaller unmarshaller;

         try {

             unmarshaller = app.getJAXBContext().createUnmarshaller();

             unmarshaller.setProperty(MEDIA_TYPE, acceptedMedia);

-            unmarshaller.setAdapter(new LinkAdapter("http://localhost:8080/JPA-RS/auction/entity/", app));

+            unmarshaller.setAdapter(new LinkAdapter(app.getBaseURI().toString(), app));

             JAXBElement<?> element = unmarshaller.unmarshal(new StreamSource(in), app.getClass(type));

             return (DynamicEntity) element.getValue();

         } catch (JAXBException e) {

@@ -394,7 +419,7 @@
                 }

             );

             marshaller.setProperty("eclipselink.media-type", mediaType);

-            marshaller.setAdapter(new LinkAdapter("http://localhost:8080/JPA-RS/auction/entity/", context));

+            marshaller.setAdapter(new LinkAdapter(context.getBaseURI().toString(), context));

             marshaller.setProperty(JAXBContext.INCLUDE_ROOT, Boolean.FALSE);

             marshaller.marshal(object, writer);

         } catch (Exception e){

@@ -415,4 +440,5 @@
         return headers;

     }

     

+    

 }