/* * Copyright (C) 2004 The Concord Consortium, Inc., * 10 Concord Crossing, Concord, MA 01742 * * Web Site: http://www.concord.org * Email: info@concord.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * END LICENSE */ package org.concord.otrunk.view; import java.awt.Component; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Vector; import org.concord.framework.otrunk.OTID; import org.concord.framework.otrunk.OTObject; import org.concord.framework.otrunk.OTObjectService; import org.concord.framework.otrunk.OTUser; import org.concord.framework.otrunk.OTrunk; import org.concord.framework.otrunk.view.OTExternalAppService; import org.concord.framework.otrunk.view.OTFrameManager; import org.concord.framework.otrunk.view.OTJComponentServiceFactory; import org.concord.framework.otrunk.view.OTUserListService; import org.concord.framework.otrunk.view.OTViewContext; import org.concord.framework.otrunk.view.OTViewFactory; import org.concord.framework.text.UserMessageHandler; import org.concord.otrunk.OTrunkImpl; import org.concord.otrunk.OTrunkServiceEntry; import org.concord.otrunk.datamodel.OTDatabase; import org.concord.otrunk.handlers.UrlStreamHandlerFactory; import org.concord.otrunk.user.OTUserObject; import org.concord.otrunk.xml.ExporterJDOM; import org.concord.otrunk.xml.XMLDatabase; import org.concord.view.PrintUserMessageHandler; import org.concord.view.SwingUserMessageHandler; public class OTViewerHelper { private OTrunk otrunk; private OTViewFactory viewFactory; private OTDatabase otDB; OTUserSession userSession; private OTDatabase userOtDB; OTFrameManagerImpl frameManager; private int userMode = OTConfig.NO_USER_MODE; private ArrayList<OTrunkServiceEntry<?>> services = new ArrayList<OTrunkServiceEntry<?>>(); URL authoredContentURL = null; public final static String ANON_SINGLE_USER_NAME = "anon_single_user"; public static OTUserObject createUser(String name, OTObjectService objService) throws Exception { OTUserObject user = (OTUserObject)objService.createObject(OTUserObject.class); user.setName(name); return user; } public static URL getURLFromArgs(String[] args) { if (args.length > 0) { // javaws will pass the -open if a jnlp is used to open a local file if (args[0].equals("-f") || args[0].equals("-open")) { if (args.length > 1) { File inFile = new File(args[1]); try { return inFile.toURL(); } catch (Exception e) { e.printStackTrace(); return null; } } } else if (args[0].equals("-r")) { if (args.length > 1) { ClassLoader cl = OTViewer.class.getClassLoader(); return cl.getResource(args[1]); } } else { String urlStr = args[0]; try { return new URL(urlStr); } catch (MalformedURLException e) { e.printStackTrace(); return null; } } } return null; } public OTViewerHelper() { if (OTConfig.getBooleanProp(OTConfig.SINGLE_USER_PROP, false)) { setUserMode(OTConfig.SINGLE_USER_MODE); } else if (OTConfig.getBooleanProp(OTConfig.NO_USER_PROP, false)) { setUserMode(OTConfig.NO_USER_MODE); } } public void init(String [] args) throws Exception { URL url = OTViewerHelper.getURLFromArgs(args); loadAuthoredContentURL(url); String userDataUrlStr = OTConfig.getStringProp(OTConfig.USERDATA_URL_PROP); if(getUserMode() == OTConfig.SINGLE_USER_MODE && userDataUrlStr != null){ URL userDataUrl = new URL(userDataUrlStr); OTDatabase otDB = loadOTDatabase(userDataUrl.openStream(), userDataUrl, System.out, false); loadUserData(otDB, null); } } public void loadOTrunkNoViewSystem(OTDatabase otDB) throws Exception { this.otDB = otDB; addService(UserMessageHandler.class, new PrintUserMessageHandler()); otrunk = new OTrunkImpl(null, otDB, services); } /** * The 2 is used so there are not two methods with the same number of Object arguments. * That would break the backward compatibility if a class was calling loadOTrunk with null * * @param systemDB * @param otDB * @throws Exception */ public void loadOTrunk2(OTDatabase systemDB, OTDatabase otDB) throws Exception { this.otDB = otDB; otrunk = new OTrunkImpl(systemDB, otDB, services); initOTrunk(otrunk); } public void initOTrunk(OTrunk ot) { this.otrunk = ot; // only update the instance field if there is a valid view factory OTViewFactory myViewFactory = (OTViewFactory) ot.getService(OTViewFactory.class); if(myViewFactory != null){ viewFactory = myViewFactory; } if(viewFactory != null){ // Maybe this shouldn't happen here frameManager = new OTFrameManagerImpl(); frameManager.setViewFactory(viewFactory); OTViewContext factoryContext = viewFactory.getViewContext(); factoryContext.addViewService(OTrunk.class, ot); factoryContext.addViewService(OTFrameManager.class, frameManager); factoryContext.addViewService(OTJComponentServiceFactory.class, new OTJComponentServiceFactoryImpl()); factoryContext.addViewService(OTExternalAppService.class, new OTExternalAppServiceImpl()); factoryContext.addViewService(OTUserListService.class, new OTUserListService() { public Vector<OTUser> getUserList() { return ((OTrunkImpl)otrunk).getUsers(); } }); ((OTViewFactoryImpl)viewFactory).contextSetupComplete(); } } /** * * @param otDB * @param parentComponent can be null if the parent component is not known. This component * is used to setup a message service which shows dialogs. In order for those dialogs to * centered on a component that component must be passed in. * @throws Exception */ public void loadOTrunk(OTDatabase otDB, Component parentComponent) throws Exception { addService(UserMessageHandler.class, new SwingUserMessageHandler(parentComponent)); loadOTrunk2(null, otDB); } public OTDatabase loadOTDatabase(URL url) throws Exception { return loadOTDatabase(url.openStream(), url, System.err, false); } public OTDatabase loadOTDatabaseXML(URL url) throws Exception { URLConnection urlConn = url.openConnection(); urlConn.setRequestProperty("Content-Type", "application/xml"); InputStream input = urlConn.getInputStream(); return loadOTDatabase(input, url); } public OTDatabase loadOTDatabase(InputStream input, URL url) throws Exception { return loadOTDatabase(input, url, System.err, false); } public void showParseException(org.jdom.input.JDOMParseException e) { String xmlWarningTitle = "XML Decoding error"; String xmlWarningMessage = "There appears to a problem parsing the XML of this document. \n" + "Please show this error message to one of the workshop leaders. \n\n" + e.getMessage(); UserMessageHandler messageHandler = getMessageHander(); messageHandler.showMessage(xmlWarningMessage, xmlWarningTitle); } public OTDatabase loadOTDatabase(InputStream input, URL url, PrintStream loggingStream, boolean trackResources) throws Exception { setupMessageHandler(); OTDatabase localOtDB; try { localOtDB = new XMLDatabase(input, url, loggingStream); ((XMLDatabase)localOtDB).setTrackResourceInfo(trackResources); ((XMLDatabase)localOtDB).loadObjects(); } catch (org.jdom.input.JDOMParseException e) { showParseException(e); throw e; } return localOtDB; } public OTDatabase loadOTDatabase(Reader reader, URL url) throws Exception { setupMessageHandler(); OTDatabase localOtDB; try { localOtDB = new XMLDatabase(reader, url, System.err); ((XMLDatabase)localOtDB).loadObjects(); } catch (org.jdom.input.JDOMParseException e) { showParseException(e); throw e; } return localOtDB; } public void saveOTDatabase(OTDatabase otDB, OutputStream output) throws Exception { ExporterJDOM.export(output, otDB.getRoot(), otDB); } public String saveOTDatabase(OTDatabase otDB) throws Exception { StringWriter userDataWriter = new StringWriter(); ExporterJDOM.export(userDataWriter, otDB.getRoot(), otDB); return userDataWriter.toString(); } public void saveOTDatabaseXML(OTDatabase otDB, URL url) throws Exception { saveOTDatabaseXML(otDB, url, "PUT"); } /** * This will send the xml of a OT Database to a url. This method sets * the content-type to "application/xml" * * The "method" parameter is only used if the connection object returned by * url.openConnection is an instanceof HttpURLConnection * * @param otDB * @param url * @param method * @throws Exception */ public void saveOTDatabaseXML(OTDatabase otDB, URL url, String method) throws Exception { URLConnection urlConn = url.openConnection(); System.out.println(urlConn); urlConn.setDoOutput(true); urlConn.setRequestProperty("Content-Type", "application/xml"); if(urlConn instanceof HttpURLConnection){ if(method == null || method.length() == 0) { method = "PUT"; } ((HttpURLConnection)urlConn).setRequestMethod(method); } // url OutputStream outStream = urlConn.getOutputStream(); ExporterJDOM.export(outStream, otDB.getRoot(), otDB); outStream.flush(); outStream.close(); InputStream response = urlConn.getInputStream(); // It seems like I have to read the response other wise the put isn't accepted. byte [] inBytes = new byte [1000]; response.read(inBytes); response.close(); if(urlConn instanceof HttpURLConnection) { ((HttpURLConnection)urlConn).disconnect(); } } /** * * @param frame * @return */ public OTViewContainerPanel createViewContainerPanel() { OTViewContainerPanel otContainer = new OTViewContainerPanel(frameManager); otContainer.setTopLevelContainer(true); otContainer.setOTViewFactory(getViewFactory()); return otContainer; } public void loadUserData(OTDatabase userOtDB, String name) throws Exception { loadUserSession(new OTMLUserSession((XMLDatabase)userOtDB, name)); } public void loadUserSession(OTUserSession userSession) throws Exception { this.userSession = userSession; userSession.setOTrunk(otrunk); userSession.load(); } /** * This does not check for unsaved user data * */ public void newAnonUserData() { newUserData(ANON_SINGLE_USER_NAME); } /** * This does not check for unsaved user data, it uses the current OTUserSession object * to create the new user data. If there is no OTUserSession object it will throw an * IllegalStateException * */ public void newUserData(String userName) { // call some new method for creating a new un-saved user state // this should set the currentUserFile to null, so the save check // prompts // for a file name if(userSession == null) { throw new IllegalStateException("a OTUserSession must be supplied before newUserData can be called"); } try { userSession.newLayer(); OTUserObject userObject = userSession.getUserObject(); if(userName != null){ userObject.setName(userName); } } catch (Exception e) { e.printStackTrace(); } } public void loadAuthoredContentURL(URL url) throws Exception { XMLDatabase systemDB = null; try { // try loading in the system object if there is one String systemOtmlUrlStr = OTConfig.getStringProp(OTConfig.SYSTEM_OTML_PROP); if(systemOtmlUrlStr != null){ URL systemOtmlUrl = new URL(systemOtmlUrlStr); // don't track the resource info on the system db. systemDB = (XMLDatabase) loadOTDatabase(systemOtmlUrl.openStream(), systemOtmlUrl, System.out, false); } } catch (Exception e){ e.printStackTrace(); systemDB = null; } boolean trackResourceInfo = getUserMode() == OTConfig.NO_USER_MODE; otDB = loadOTDatabase(url.openStream(), url, System.out, trackResourceInfo); loadOTrunk2(systemDB, otDB); authoredContentURL = url; } protected UserMessageHandler setupMessageHandler() { // Check to see if we need to add a user message handler ourselves UserMessageHandler messageHandler = (UserMessageHandler) findService(UserMessageHandler.class); if(messageHandler == null){ messageHandler = new PrintUserMessageHandler(); addService(UserMessageHandler.class, messageHandler); } return messageHandler; } /** * Find the registered message handler, add a PrintUserMessageHandler. * * @return */ public UserMessageHandler getMessageHander() { return setupMessageHandler(); } @SuppressWarnings("unchecked") public <T> T findService(Class<T> serviceInterface) { for(int i=0; i<services.size(); i++){ OTrunkServiceEntry<?> entry = services.get(i); if(serviceInterface.equals(entry.serviceInterface)){ return (T)entry.service; } } return null; } public OTObject getAuthoredRoot() throws Exception { String rootLocalId = OTConfig.getStringProp(OTConfig.ROOT_OBJECT_PROP); if (rootLocalId != null && otDB instanceof XMLDatabase) { OTID rootID = ((XMLDatabase)otDB).getOTIDFromLocalID(rootLocalId); return otrunk.getOTObject(rootID); } return otrunk.getRoot(); } public OTObject getRootObject() throws Exception { OTObject root = getAuthoredRoot(); OTUser user = getCurrentUser(); if(user != null){ return getOtrunk().getUserRuntimeObject(root, user); } return root; } public OTrunk getOtrunk() { return otrunk; } public OTViewFactory getViewFactory() { return viewFactory; } public OTDatabase getOtDB() { return otDB; } public OTDatabase getUserOtDB() { return userOtDB; } public OTUserSession getUserSession() { return userSession; } public OTUser getCurrentUser() { if(userSession == null) { return null; } return userSession.getUserObject(); } public void setUserMode(int userMode) { this.userMode = userMode; } public int getUserMode() { return userMode; } /** * this needs to be called before initialized. * * @param serviceInterface * @param service */ public <T> void addService(Class<T> serviceInterface, T service) { OTrunkServiceEntry<T> entry = new OTrunkServiceEntry<T>(service, serviceInterface); services.add(entry); } static { try { UrlStreamHandlerFactory.registerHandlerClass(org.concord.otrunk.handlers.jres.Handler.class); } catch (Exception e){ // don't do anything here. If SailStreamHandlerFactory has already been // created, SailOTViewer should have already added the handlers } } }