/* ******************************************************************** Licensed to Jasig under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. Jasig licenses this file to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.bedework.calsvc; import org.bedework.caldav.server.soap.synch.SynchConnection; import org.bedework.caldav.server.soap.synch.SynchConnectionsMBean; import org.bedework.calfacade.BwCalendar; import org.bedework.calfacade.configs.SynchConfig; import org.bedework.calfacade.exc.CalFacadeException; import org.bedework.calfacade.synch.BwSynchInfo; import org.bedework.calsvci.CalendarsI.CheckSubscriptionResult; import org.bedework.calsvci.CalendarsI.SynchStatusResponse; import org.bedework.calsvci.SynchI; import org.bedework.synch.wsmessages.ActiveSubscriptionRequestType; import org.bedework.synch.wsmessages.ArrayOfSynchProperties; import org.bedework.synch.wsmessages.ConnectorInfoType; import org.bedework.synch.wsmessages.GetInfoRequestType; import org.bedework.synch.wsmessages.GetInfoResponseType; import org.bedework.synch.wsmessages.SubscribeRequestType; import org.bedework.synch.wsmessages.SubscribeResponseType; import org.bedework.synch.wsmessages.SubscriptionStatusRequestType; import org.bedework.synch.wsmessages.SynchDirectionType; import org.bedework.synch.wsmessages.SynchEndType; import org.bedework.synch.wsmessages.SynchIdTokenType; import org.bedework.synch.wsmessages.SynchMasterType; import org.bedework.synch.wsmessages.SynchPropertyType; import org.bedework.synch.wsmessages.SynchRemoteService; import org.bedework.synch.wsmessages.SynchRemoteServicePortType; import org.bedework.synch.wsmessages.UnsubscribeRequestType; import org.bedework.synch.wsmessages.UnsubscribeResponseType; import org.bedework.util.jmx.MBeanUtil; import org.oasis_open.docs.ws_calendar.ns.soap.StatusType; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; /** Handles interactions with the synch engine from within bedework. * * @author Mike Douglass douglm - rpi.edu */ class Synch extends CalSvcDb implements SynchI { private SynchConnectionsMBean conns; //private ObjectFactory of = new ObjectFactory(); private final SynchConfig synchConf; static final String synchConnectionsMBeanName = SynchConnectionsMBean.serviceName; /** Namespace of the synch SOAP service */ static final String synchNamespace = "http://www.bedework.org/synch/wsmessages"; //static final String synchManagerUriPname = "synchManagerUri"; //static final String synchWsdlUriPname = "synchWsdlUri"; //static final String synchConnectorIdPname = "synchConnectorId"; static final QName synchServicename = new QName(synchNamespace, "SynchRemoteService"); /* Refetched periodically but cached */ private static volatile BwSynchInfo synchInfo; private static final long synchInfoRefreshPeriod = 1000 * 60 * 5; // every 5 mins private static volatile long lastSynchInfoRefresh; Synch(final CalSvc svci, final SynchConfig synchConf) { super(svci); this.synchConf = synchConf; } @Override public boolean getActive() throws CalFacadeException { return getSynchConnection() != null; } private static class SConnection implements Connection { private final SynchConnection sc; SConnection(final SynchConnection sc) { this.sc = sc; } } @Override public Connection getSynchConnection() throws CalFacadeException { try { return new SConnection(getActiveSynchConnections(). getConnectionById(synchConf.getConnectorId())); } catch (final Throwable t) { warn("Exception getting synch connection: " + "is the synch engine installed and running?"); if (debug) { error(t); } return null; } } @Override public boolean subscribe(final BwCalendar val) throws CalFacadeException { final SConnection sconn = (SConnection)getSynchConnection(); if ((sconn == null) || (sconn.sc == null)) { throw new CalFacadeException("No active synch connection"); } final SynchConnection sc = sconn.sc; final SubscribeRequestType subreq = new SubscribeRequestType(); subreq.setToken(sc.getSynchToken()); subreq.setPrincipalHref(val.getOwnerHref()); // We'll be A other end is B subreq.setDirection(SynchDirectionType.B_TO_A); subreq.setMaster(SynchMasterType.B); // We'll make this up for the moment - needs to be handed to us by front end /* =============== End A - bedework ======================= */ final ConnectorInfoType ciA = new ConnectorInfoType(); ciA.setConnectorId(synchConf.getConnectorId()); final ArrayOfSynchProperties aosA = new ArrayOfSynchProperties(); ciA.setProperties(aosA); aosA.getProperty().add(makeSynchProperty("uri", val.getPath())); aosA.getProperty().add(makeSynchProperty("principal", getPrincipal().getPrincipalRef())); aosA.getProperty().add(makeSynchProperty("opaque-data", makeOpaqueData(val))); subreq.setEndAConnector(ciA); /* =============== End B - file ======================= */ final ConnectorInfoType ciB = new ConnectorInfoType(); ciB.setConnectorId("read-only-file"); final ArrayOfSynchProperties aosB = new ArrayOfSynchProperties(); ciB.setProperties(aosB); aosB.getProperty().add(makeSynchProperty("uri", val.getAliasUri())); if (val.getRemoteId() != null) { aosB.getProperty().add(makeSynchProperty("principal", val.getRemoteId())); aosB.getProperty().add(makeSynchProperty("password", val.getRemotePw())); } int refreshRate = val.getRefreshRate(); // seconds if (refreshRate == 0) { refreshRate = 60; } aosB.getProperty().add(makeSynchProperty("refreshDelay", String.valueOf(refreshRate * 1000))); subreq.setEndBConnector(ciB); /* =============== Global subscription properties ======================= */ final ArrayOfSynchProperties aos = new ArrayOfSynchProperties(); subreq.setInfo(aos); aos.getProperty().add(makeSynchProperty("alarm-processing", "REMOVE")); aos.getProperty().add(makeSynchProperty("scheduling-processing", "REMOVE")); if (val.getSynchXlocXcontacts()) { aos.getProperty().add(makeSynchProperty("xlocxcontacts", "true")); } if (val.getSynchXcategories()) { aos.getProperty().add(makeSynchProperty("xcategories", "true")); } final SubscribeResponseType sresp = getPort(synchConf.getManagerUri()).subscribe( getIdToken(getPrincipal().getPrincipalRef(), sc), subreq); if (sresp.getStatus() != StatusType.OK) { return false; } val.setSubscriptionId(sresp.getSubscriptionId()); return true; } @Override public boolean unsubscribe(final BwCalendar val, final boolean forDelete) throws CalFacadeException { if (val.getSubscriptionId() == null) { return true; // just noop it } final SConnection sconn = (SConnection)getSynchConnection(); if ((sconn == null) || (sconn.sc == null)) { throw new CalFacadeException("No active synch connection"); } final SynchConnection sc = sconn.sc; final UnsubscribeRequestType usreq = (UnsubscribeRequestType)makeAsr(val, new UnsubscribeRequestType(), sc.getSynchToken()); final UnsubscribeResponseType usresp = getPort(synchConf.getManagerUri()).unsubscribe( getIdToken(getPrincipal().getPrincipalRef(), sc), usreq); if (usresp.getStatus() != StatusType.OK) { return false; } if (!forDelete) { // It's always delete at the moment - this is an Oracle LONG workround val.setSubscriptionId(null); } return true; } @Override public SynchStatusResponse getSynchStatus(final BwCalendar val) throws CalFacadeException { final SynchStatusResponse ssr = new SynchStatusResponse(); if (val == null) { ssr.requestStatus = CheckSubscriptionResult.notFound; return ssr; } if (!val.getExternalSub()) { ssr.requestStatus = CheckSubscriptionResult.notExternal; return ssr; } if (val.getSubscriptionId() == null) { ssr.requestStatus = CheckSubscriptionResult.notsubscribed; return ssr; } final SConnection sconn = (SConnection)getSynchConnection(); if ((sconn == null) || (sconn.sc == null)) { ssr.requestStatus = CheckSubscriptionResult.noSynchService; return ssr; } final SynchConnection sc = sconn.sc; final SubscriptionStatusRequestType ssreq = (SubscriptionStatusRequestType)makeAsr(val, new SubscriptionStatusRequestType(), sc.getSynchToken()); ssr.subscriptionStatus = getPort(synchConf.getManagerUri()).subscriptionStatus( getIdToken(getPrincipal().getPrincipalRef(), sc), ssreq); if (ssr.subscriptionStatus.getStatus() == StatusType.NOT_FOUND) { ssr.requestStatus = CheckSubscriptionResult.notsubscribed; } else { ssr.requestStatus = CheckSubscriptionResult.ok; } return ssr; } @Override public CheckSubscriptionResult checkSubscription(final BwCalendar val) throws CalFacadeException { final SynchStatusResponse ssr = getSynchStatus(val); if (ssr.requestStatus == CheckSubscriptionResult.notsubscribed) { // Try to resubscribe if (subscribe(val)) { return CheckSubscriptionResult.resubscribed; } return CheckSubscriptionResult.failed; } if (ssr.requestStatus != CheckSubscriptionResult.ok) { return ssr.requestStatus; } if (ssr.subscriptionStatus.getStatus() == StatusType.OK) { // Assume all is fine return CheckSubscriptionResult.ok; } return CheckSubscriptionResult.failed; } @Override public BwSynchInfo getSynchInfo() throws CalFacadeException { if ((synchInfo != null) && ((System.currentTimeMillis() - lastSynchInfoRefresh) < synchInfoRefreshPeriod)) { return synchInfo; } lastSynchInfoRefresh = System.currentTimeMillis(); // We're doing it final SConnection sconn = (SConnection)getSynchConnection(); if ((sconn == null) || (sconn.sc == null)) { warn("No active synch connection"); return null; } final SynchConnection sc = sconn.sc; GetInfoResponseType girt = null; try { girt = getPort(synchConf.getManagerUri()).getInfo( getIdToken(getPrincipal().getPrincipalRef(), sc), new GetInfoRequestType()); } catch (final Throwable t){ error(t); warn("Unable to fetch synch info"); return null; } if ((girt == null) || (girt.getInfo() == null)) { warn("Unable to fetch synch info"); return null; } synchInfo = BwSynchInfo.copy(girt.getInfo()); return synchInfo; } /* ==================================================================== * private methods * ==================================================================== */ private SynchConnectionsMBean getActiveSynchConnections() throws CalFacadeException { try { if (conns == null) { conns = (SynchConnectionsMBean)MBeanUtil. getMBean(SynchConnectionsMBean.class, synchConnectionsMBeanName); } return conns; } catch (final Throwable t) { throw new CalFacadeException(t); } } private ActiveSubscriptionRequestType makeAsr(final BwCalendar val, final ActiveSubscriptionRequestType asr, final String synchToken) throws CalFacadeException { if (val.getSubscriptionId() == null) { return null; // not active } asr.setToken(synchToken); asr.setPrincipalHref(val.getOwnerHref()); asr.setSubscriptionId(val.getSubscriptionId()); asr.setEnd(SynchEndType.A); asr.setConnectorInfo(makeCi(val)); return asr; } private ConnectorInfoType makeCi(final BwCalendar val) throws CalFacadeException { final ConnectorInfoType ci = new ConnectorInfoType(); ci.setConnectorId(synchConf.getConnectorId()); final ArrayOfSynchProperties aosA = new ArrayOfSynchProperties(); ci.setProperties(aosA); aosA.getProperty().add(makeSynchProperty("uri", val.getPath())); aosA.getProperty().add(makeSynchProperty("principal", getPrincipal().getPrincipalRef())); return ci; } private SynchPropertyType makeSynchProperty(final String name, final String value) { final SynchPropertyType sp = new SynchPropertyType(); sp.setName(name); sp.setValue(value); return sp; } // private String getSynchUri() throws CalFacadeException { //return (String)getGlobalProperty(synchManagerUriPname); //} SynchIdTokenType getIdToken(final String principal, final SynchConnection sc) throws CalFacadeException { final SynchIdTokenType idToken = new SynchIdTokenType(); idToken.setPrincipalHref(principal); idToken.setSubscribeUrl(sc.getSubscribeUrl()); idToken.setSynchToken(sc.getSynchToken()); return idToken; } private String makeOpaqueData(final BwCalendar col) throws CalFacadeException { return "public-admin=" + this.isPublicAdmin() + "\t" + "adminCreateEprops=" + col.getSynchAdminCreateEprops(); } SynchRemoteServicePortType getPort(final String uri) throws CalFacadeException { try { final URL wsURL = new URL(synchConf.getWsdlUri()); final SynchRemoteService ers = new SynchRemoteService(wsURL, synchServicename); final SynchRemoteServicePortType port = ers.getSynchRSPort(); // Override the endpoint address ((BindingProvider)port).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, uri); return port; } catch (final Throwable t) { throw new CalFacadeException(t); } } }