/*************************************************************************** * Copyright 2006-2011 (C) by Fabrizio Montesi <famontesi@gmail.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.net.ports; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import jolie.lang.Constants; import jolie.process.AssignmentProcess; import jolie.process.NullProcess; import jolie.process.Process; import jolie.runtime.AbstractIdentifiableObject; import jolie.runtime.Value; import jolie.runtime.VariablePath; import jolie.Interpreter; import jolie.net.CommChannel; import jolie.net.CommMessage; import jolie.net.protocols.CommProtocol; import jolie.process.SequentialProcess; import jolie.runtime.expression.Expression; import jolie.runtime.VariablePathBuilder; import jolie.util.LocationParser; /** * This class represents a JOLIE output port, offering methods for getting * proper communication channels for it. * * @author Fabrizio Montesi */ public class OutputPort extends AbstractIdentifiableObject implements Port { private final Interpreter interpreter; private final Process configurationProcess; private Expression locationExpression; private final VariablePath locationVariablePath, protocolVariablePath; private final boolean isConstant; private final Interface iface; private final boolean messageBus; /* To be called at runtime, after main is run. * Requires the caller to set the variables by itself. */ public OutputPort( Interpreter interpreter, String id ) { super( id ); this.interpreter = interpreter; this.protocolVariablePath = new VariablePathBuilder( false ) .add( id(), 0 ) .add( Constants.PROTOCOL_NODE_NAME, 0 ) .toVariablePath(); this.locationVariablePath = new VariablePathBuilder( false ) .add( id(), 0 ) .add( Constants.LOCATION_NODE_NAME, 0 ) .toVariablePath(); this.locationExpression = this.locationVariablePath; this.configurationProcess = null; this.isConstant = false; this.iface = Interface.UNDEFINED; this.messageBus = false; } public OutputPort( Interpreter interpreter, String id, VariablePath locationVariablePath, VariablePath protocolVariablePath, Interface iface, boolean isConstant ) { super( id ); this.isConstant = isConstant; this.interpreter = interpreter; this.locationVariablePath = locationVariablePath; this.protocolVariablePath = protocolVariablePath; this.iface = iface; this.locationExpression = locationVariablePath; this.configurationProcess = NullProcess.getInstance(); this.messageBus = false; } /** * To be called by OOITBuilder */ public OutputPort( Interpreter interpreter, String id, String protocolId, Process protocolConfigurationProcess, URI locationURI, Interface iface, boolean isConstant, boolean messageBus ) { super( id ); this.isConstant = isConstant; this.interpreter = interpreter; this.iface = iface; this.protocolVariablePath = new VariablePathBuilder( false ) .add( id(), 0 ) .add( Constants.PROTOCOL_NODE_NAME, 0 ) .toVariablePath(); this.locationVariablePath = new VariablePathBuilder( false ) .add( id(), 0 ) .add( Constants.LOCATION_NODE_NAME, 0 ) .toVariablePath(); this.locationExpression = locationVariablePath; // Create the configuration Process Process a = ( locationURI == null ) ? NullProcess.getInstance() : new AssignmentProcess( this.locationVariablePath, Value.create( locationURI.toString() ) ); List< Process > children = new LinkedList< Process >(); children.add( a ); if ( protocolId != null ) { children.add( new AssignmentProcess( this.protocolVariablePath, Value.create( protocolId ) ) ); } children.add( protocolConfigurationProcess ); this.configurationProcess = new SequentialProcess( children.toArray( new Process[ children.size() ] ) ); this.messageBus = messageBus; } /** * Returns a new message with same operation and value, but resourcePath * updated to the current one of this output port. * @param message the original message * @return a new message with same operation and value, but updated resource */ public CommMessage getResourceUpdatedMessage( CommMessage message ) throws URISyntaxException { return new CommMessage( message.id(), message.operationName(), getResourcePath(), message.value(), message.fault() ); } public Interface getInterface() { return iface; } public void optimizeLocation() { if ( isConstant ) { locationExpression = locationVariablePath.getValue(); } } public VariablePath protocolConfigurationPath() { return protocolVariablePath; } /** * Gets the protocol to be used for communicating with this output port. * @return the protocol to be used for communicating with this output port. * @throws java.io.IOException * @throws java.net.URISyntaxException */ public synchronized CommProtocol getProtocol() throws IOException, URISyntaxException { String protocolId = protocolVariablePath.getValue().strValue(); if ( protocolId.isEmpty() ) { throw new IOException( "Unspecified protocol for output port " + id() ); } return interpreter.commCore().createOutputCommProtocol( protocolId, protocolVariablePath, new URI( locationExpression.evaluate().strValue() ) ); } private synchronized CommChannel getCommChannel( boolean forceNew ) throws URISyntaxException, IOException { CommChannel ret = null; Value loc = locationExpression.evaluate(); if ( loc.isChannel() ) { // It's a local channel ret = loc.channelValue(); if ( forceNew ) { ret = ret.createDuplicate(); } } else { URI uri = getLocation( loc ); if ( forceNew ) { // A fresh channel was requested ret = interpreter.commCore().createCommChannel( uri, this ); } else { // Try reusing an existing channel first String protocol = protocolVariablePath.getValue().strValue(); ret = interpreter.commCore().getPersistentChannel( uri, protocol ); if ( ret == null ) { ret = interpreter.commCore().createCommChannel( uri, this ); } } } ret.setParentOutputPort( this ); return ret; } private static class LazyLocalUriHolder { private LazyLocalUriHolder() {} private static final URI uri = URI.create( "local" ); } private static final Map< String, URI > uriCache = new WeakHashMap< String, URI > (); /** * Returns the resource path of the location of this output port. * @return the resource path of the location of this output port * @throws java.net.URISyntaxException */ public String getResourcePath() throws URISyntaxException { Value location = locationExpression.evaluate(); if ( location.isChannel() ) { return "/"; } return LocationParser.getResourcePath( getLocation( location ) ); } private URI getLocation( Value location ) throws URISyntaxException { if ( location.isChannel() ) { return LazyLocalUriHolder.uri; } String s = location.strValue(); URI ret; if ( (ret=uriCache.get( s )) == null ) { ret = new URI( s ); uriCache.put( s, ret ); } return ret; } /** * Returns a new and unused CommChannel for this OutputPort * @return a CommChannel for this OutputPort * @throws java.net.URISyntaxException * @throws java.io.IOException */ public final CommChannel getNewCommChannel() throws URISyntaxException, IOException { return getCommChannel( true ); } /** * Returns a CommChannel for this OutputPort, possibly reusing an * open persistent channel. * @return a CommChannel for this OutputPort * @throws java.net.URISyntaxException * @throws java.io.IOException */ public final CommChannel getCommChannel() throws URISyntaxException, IOException { return getCommChannel( false ); } /** * Returns the location variable path of this output port. * @return the location variable path of this output port */ public VariablePath locationVariablePath() { return locationVariablePath; } /** * Returns the protocol configuration process of this output port. * @return the protocol configuration process of this output port */ public Process configurationProcess() { return configurationProcess; } /** * Returns true if the port is a messageBus port. * @return Returns true if the port is a messageBus port. */ public boolean messageBus(){ return messageBus; } }