Updated calendar server code. Added intent for bi-dirctional path. Added latency as one more parameter in constraints.

Added exception catch for parsing parameters.

Fixed Jenkins checking errors.

Use intent key and 2way intent P2P setup.

Change-Id: Ib2bd25deb793b2a34179cbf8794ca5aa3e427f91
diff --git a/apps/calendar/src/main/java/org/onosproject/calendar/BandwidthCalendarResource.java b/apps/calendar/src/main/java/org/onosproject/calendar/BandwidthCalendarResource.java
index 9d457b7..2143e89 100644
--- a/apps/calendar/src/main/java/org/onosproject/calendar/BandwidthCalendarResource.java
+++ b/apps/calendar/src/main/java/org/onosproject/calendar/BandwidthCalendarResource.java
@@ -15,42 +15,50 @@
  */
 package org.onosproject.calendar;
 
-import java.net.URI;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
+import org.onlab.packet.Ethernet;
+import org.onlab.rest.BaseResource;
+import org.onlab.util.Tools;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.HostToHostIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentEvent;
 import org.onosproject.net.intent.IntentListener;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
-import org.onlab.rest.BaseResource;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.TwoWayP2PIntent;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
+import org.onosproject.net.resource.Bandwidth;
+import org.slf4j.Logger;
 
-import javax.ws.rs.POST;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Response;
-
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.intent.PointToPointIntent;
-import org.onlab.packet.Ethernet;
+import java.net.URI;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import static org.onosproject.net.PortNumber.portNumber;
 import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
-
-import static org.onosproject.net.intent.IntentState.FAILED;
-import static org.onosproject.net.intent.IntentState.INSTALLED;
-import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+import static org.onosproject.net.intent.IntentState.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
-import org.slf4j.Logger;
-
 /**
  * Web resource for triggering calendared intents.
  */
@@ -58,80 +66,262 @@
 public class BandwidthCalendarResource extends BaseResource {
 
     private static final Logger log = getLogger(BandwidthCalendarResource.class);
-    private static final long TIMEOUT = 5; // seconds
+    private static final long TIMEOUT = 10; // seconds
 
-    @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
+    private static final String INVALID_PARAMETER = "INVALID_PARAMETER\n";
+    private static final String OPERATION_INSTALLED = "INSTALLED\n";
+    private static final String OPERATION_FAILED = "FAILED\n";
+    private static final String OPERATION_WITHDRAWN = "WITHDRAWN\n";
+
+    /**
+     * Setup a bi-directional path with constraints between switch to switch.
+     * Switch is identified by DPID.
+     *
+     * @param src the path source (DPID or hostID)
+     * @param dst the path destination (DPID or hostID)
+     * @param srcPort the source port (-1 if src/dest is a host)
+     * @param dstPort the destination port (-1 if src/dest is a host)
+     * @param bandwidth the bandwidth (mbps) requirement for the path
+     * @param latency the latency (micro sec) requirement for the path
+     * @return intent key if successful,
+     *         server error message or "FAILED" if failed to create or submit intent
+     */
+    @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}/{latency}")
     @POST
-    public Response createIntent(@PathParam("src") String src,
-                                 @PathParam("dst") String dst,
-                                 @PathParam("srcPort") String srcPort,
-                                 @PathParam("dstPort") String dstPort,
-                                 @PathParam("bandwidth") String bandwidth) {
+    // TODO could allow applications to provide optional key
+    // ... if you do, you will need to change from LongKeys to StringKeys
+    public Response setupPath(@PathParam("src") String src,
+                              @PathParam("dst") String dst,
+                              @PathParam("srcPort") String srcPort,
+                              @PathParam("dstPort") String dstPort,
+                              @PathParam("bandwidth") String bandwidth,
+                              @PathParam("latency") String latency) {
 
-        log.info("Receiving Create Intent request...");
-        log.info("Path Constraints: Src = {} SrcPort = {} Dest = {} DestPort = {} BW = {}",
-                 src, srcPort, dst, dstPort, bandwidth);
+        log.info("Path Constraints: Src = {} SrcPort = {} Dest = {} DestPort = {} " +
+                          "BW = {} latency = {}",
+                 src, srcPort, dst, dstPort, bandwidth, latency);
+
+        if (src == null || dst == null || srcPort == null || dstPort == null) {
+            return Response.ok(INVALID_PARAMETER).build();
+        }
+
+        Long bandwidthL = 0L;
+        Long latencyL = 0L;
+        try {
+            bandwidthL = Long.parseLong(bandwidth, 10);
+            latencyL = Long.parseLong(latency, 10);
+        } catch (Exception e) {
+            return Response.ok(INVALID_PARAMETER).build();
+        }
+
+        Intent intent = createIntent(null, src, dst, srcPort, dstPort, bandwidthL, latencyL);
+        try {
+            if (submitIntent(intent)) {
+                return Response.ok(intent.key() + "\n").build();
+            } else {
+                return Response.ok(OPERATION_FAILED).build();
+            }
+        } catch (Exception e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+    /**
+     * Modify a bi-directional path's bandwidth.
+     *
+     * @param intentKey the path intent key
+     * @param src the path source (DPID or hostID)
+     * @param dst the path destination (DPID or hostID)
+     * @param srcPort the source port (-1 if src/dest is a host)
+     * @param dstPort the destination port (-1 if src/dest is a host)
+     * @param bandwidth the bandwidth (mbps) requirement for the path
+     * @return @return Intent state, "INSTALLED", if successful,
+     *         server error message or "FAILED" if failed to modify any direction intent
+     */
+    @javax.ws.rs.Path("/{intentKey}/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
+    @PUT
+    public Response modifyBandwidth(@PathParam("intentKey") String intentKey,
+                                    @PathParam("src") String src,
+                                    @PathParam("dst") String dst,
+                                    @PathParam("srcPort") String srcPort,
+                                    @PathParam("dstPort") String dstPort,
+                                    @PathParam("bandwidth") String bandwidth) {
+
+        log.info("Modify bw for intentKey = {}; src = {}; dst = {};" +
+                         "srcPort = {}; dstPort = {}; with new bandwidth = {}",
+                 intentKey, src, dst, srcPort, dstPort, bandwidth);
+
+        if (src == null || dst == null || srcPort == null || dstPort == null) {
+            return Response.ok(INVALID_PARAMETER).build();
+        }
+
+        Long bandwidthL = 0L;
+        try {
+            bandwidthL = Long.parseLong(bandwidth, 10);
+        } catch (Exception e) {
+            return Response.ok(INVALID_PARAMETER).build();
+        }
 
         IntentService service = get(IntentService.class);
+        Intent originalIntent
+                = service.getIntent(Key.of(Tools.fromHex(intentKey.replace("0x", "")), appId()));
 
-        ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
-        ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
+        if (originalIntent == null) {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+
+        // get the latency constraint from the original intent
+        Long latencyL = 0L;
+        if (originalIntent instanceof ConnectivityIntent) {
+            ConnectivityIntent connectivityIntent = (ConnectivityIntent) originalIntent;
+            for (Constraint constraint : connectivityIntent.constraints()) {
+                if (constraint instanceof LatencyConstraint) {
+                    latencyL = ((LatencyConstraint) constraint).latency().get(ChronoUnit.MICROS);
+                }
+            }
+        }
+
+        Intent newIntent = createIntent(originalIntent.key(), src, dst,
+                                        srcPort, dstPort, bandwidthL, latencyL);
+        try {
+            if (submitIntent(newIntent)) {
+                return Response.ok(OPERATION_INSTALLED).build();
+            } else {
+                return Response.ok(OPERATION_FAILED).build();
+            }
+        } catch (Exception e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+
+    /**
+     * Create an Intent for a bidirectional path with constraints.
+     *
+     * @param key optional intent key
+     * @param src the path source (DPID or hostID)
+     * @param dst the path destination (DPID or hostID)
+     * @param srcPort the source port (-1 if src/dest is a host)
+     * @param dstPort the destination port (-1 if src/dest is a host)
+     * @param bandwidth the bandwidth (mbps) requirement for the path
+     * @param latency the latency (micro sec) requirement for the path
+     * @return the appropriate intent
+     */
+    private Intent createIntent(Key key,
+                                String src,
+                                String dst,
+                                String srcPort,
+                                String dstPort,
+                                Long bandwidth,
+                                Long latency) {
 
         TrafficSelector selector = buildTrafficSelector();
         TrafficTreatment treatment = builder().build();
 
-        PointToPointIntent intentP2P =
-                new PointToPointIntent(appId(), selector, treatment,
-                                       srcPoint, dstPoint);
+        final Constraint constraintBandwidth =
+                new BandwidthConstraint(Bandwidth.mbps(bandwidth));
+        final Constraint constraintLatency =
+                new LatencyConstraint(Duration.of(latency, ChronoUnit.MICROS));
+        final List<Constraint> constraints = new LinkedList<>();
+
+        constraints.add(constraintBandwidth);
+        constraints.add(constraintLatency);
+
+        if (srcPort.equals("-1")) {
+            HostId srcPoint = HostId.hostId(src);
+            HostId dstPoint = HostId.hostId(dst);
+            return new HostToHostIntent(appId(), key, srcPoint, dstPoint,
+                                        selector, treatment, constraints);
+        } else {
+            ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
+            ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
+            return new TwoWayP2PIntent(appId(), key, srcPoint, dstPoint,
+                                       selector, treatment, constraints);
+        }
+    }
+
+
+    /**
+     * Synchronously submits an intent to the Intent Service.
+     *
+     * @param intent intent to submit
+     * @return true if operation succeed, false otherwise
+     */
+    private boolean submitIntent(Intent intent)
+        throws InterruptedException {
+        IntentService service = get(IntentService.class);
 
         CountDownLatch latch = new CountDownLatch(1);
-        InternalIntentListener listener = new InternalIntentListener(intentP2P, service, latch);
+        InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
         service.addListener(listener);
-        service.submit(intentP2P);
-        try {
-            if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
-                log.info("Submitted Calendar App intent: src = {}; dst = {}; " +
-                                 "srcPort = {}; dstPort = {}; intentID = {}",
-                         src, dst, srcPort, dstPort, intentP2P.id());
-                String reply = intentP2P.id() + " " + listener.getState() + "\n";
-                return Response.ok(reply).build();
-            }
-        } catch (InterruptedException e) {
-            log.warn("Interrupted while waiting for intent {} status", intentP2P.id());
+        service.submit(intent);
+        log.info("Submitted Calendar App intent and waiting: {}", intent);
+        if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
+                listener.getState() == INSTALLED) {
+            return true;
         }
-        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        return false;
     }
 
-    @javax.ws.rs.Path("/cancellation/{intentId}")
+    /**
+     * Remove a bi-directional path with created intent key.
+     *
+     * @param intentKey the string key for the intent to remove
+     * @return Intent state, "WITHDRAWN", if successful,
+     *         server error message or FAILED" if any direction intent remove failed
+     */
+    @javax.ws.rs.Path("/{intentKey}")
     @DELETE
-    public Response withdrawIntent(@PathParam("intentId") String intentId) {
-        log.info("Receiving Teardown request for {}", intentId);
-        IntentService service = get(IntentService.class);
-        // TODO: there needs to be an app id and key here
-        /*
-        Intent intent = service.getIntent(IntentId.valueOf(Long.parseLong(intentId)));
-        if (intent != null) {
-            service.withdraw(intent);
-            String reply = "ok\n";
-            return Response.ok(reply).build();
+    public Response removePath(@PathParam("intentKey") String intentKey) {
+
+        log.info("Receiving tear down request for {}", intentKey);
+
+        if (intentKey == null) {
+            return Response.ok(INVALID_PARAMETER).build();
         }
-        */
-        return Response.status(Response.Status.NOT_FOUND).build();
+
+        IntentService service = get(IntentService.class);
+        Intent intent = service.getIntent(Key.of(Tools.fromHex(intentKey.replace("0x", "")), appId()));
+
+        if (intent == null) {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+
+        try {
+            if (withdrawIntent(intent)) {
+                return Response.ok(OPERATION_WITHDRAWN).build();
+            } else {
+                return Response.ok(OPERATION_FAILED).build();
+            }
+        } catch (Exception e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
     }
 
-    @javax.ws.rs.Path("/modification/{intentId}/{bandwidth}")
-    @POST
-    public Response modifyBandwidth(@PathParam("intentId") String intentId,
-                                    @PathParam("bandwidth") String bandwidth) {
+    /**
+     * Synchronously withdraws an intent to the Intent Service.
+     *
+     * @param intent intent to submit
+     * @return true if operation succeed, false otherwise
+     */
+    private boolean withdrawIntent(Intent intent)
+            throws InterruptedException {
+        IntentService service = get(IntentService.class);
 
-        log.info("Receiving Modify request...");
-        log.info("Modify bw for intentId = {} with new bandwidth = {}", intentId, bandwidth);
-
-        String reply = "ok\n";
-        return Response.ok(reply).build();
+        CountDownLatch latch = new CountDownLatch(1);
+        InternalIntentListener listener = new InternalIntentListener(intent, service, latch);
+        service.addListener(listener);
+        service.withdraw(intent);
+        log.info("Withdrawing intent and waiting: {}", intent);
+        if (latch.await(TIMEOUT, TimeUnit.SECONDS) &&
+                listener.getState() == WITHDRAWN) {
+            return true;
+        }
+        return false;
     }
 
-    private TrafficSelector buildTrafficSelector() {
+
+    private static TrafficSelector buildTrafficSelector() {
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
         Short ethType = Ethernet.TYPE_IPV4;
 
@@ -140,7 +330,7 @@
         return selectorBuilder.build();
     }
 
-    private DeviceId deviceId(String dpid) {
+    private static DeviceId deviceId(String dpid) {
         return DeviceId.deviceId(URI.create("of:" + dpid));
     }
 
@@ -168,8 +358,8 @@
                 state = service.getIntentState(intent.key());
                 if (state == INSTALLED || state == FAILED || state == WITHDRAWN) {
                     latch.countDown();
+                    service.removeListener(this);
                 }
-                service.removeListener(this);
             }
         }
 
diff --git a/core/api/src/main/java/org/onosproject/net/intent/TwoWayP2PIntent.java b/core/api/src/main/java/org/onosproject/net/intent/TwoWayP2PIntent.java
new file mode 100644
index 0000000..707ee6c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/intent/TwoWayP2PIntent.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.intent;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.constraint.LinkTypeConstraint;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstraction of bidirectional connectivity between two points in the network.
+ */
+public final class TwoWayP2PIntent extends ConnectivityIntent {
+
+    private final ConnectPoint one;
+    private final ConnectPoint two;
+
+    /**
+     * Creates a new host-to-host intent with the supplied host pair and no
+     * other traffic selection or treatment criteria.
+     *
+     * @param appId     application identifier
+     * @param one       first host
+     * @param two       second host
+     * @throws NullPointerException if {@code one} or {@code two} is null.
+     */
+    public TwoWayP2PIntent(ApplicationId appId, ConnectPoint one, ConnectPoint two) {
+        this(appId, one, two,
+             DefaultTrafficSelector.builder().build(),
+             DefaultTrafficTreatment.builder().build(),
+             ImmutableList.of(new LinkTypeConstraint(false, Link.Type.OPTICAL)));
+    }
+
+    /**
+     * Creates a new host-to-host intent with the supplied host pair.
+     *
+     * @param appId     application identifier
+     * @param one       first host
+     * @param two       second host
+     * @param selector  action
+     * @param treatment ingress port
+     * @throws NullPointerException if {@code one} or {@code two} is null.
+     */
+    public TwoWayP2PIntent(ApplicationId appId, ConnectPoint one, ConnectPoint two,
+                           TrafficSelector selector,
+                           TrafficTreatment treatment) {
+        this(appId, one, two, selector, treatment,
+             ImmutableList.of(new LinkTypeConstraint(false, Link.Type.OPTICAL)));
+    }
+
+    /**
+     * Creates a new host-to-host intent with the supplied host pair.
+     *
+     * @param appId       application identifier
+     * @param one         first host
+     * @param two         second host
+     * @param selector    action
+     * @param treatment   ingress port
+     * @param constraints optional prioritized list of path selection constraints
+     * @throws NullPointerException if {@code one} or {@code two} is null.
+     */
+    public TwoWayP2PIntent(ApplicationId appId, ConnectPoint one, ConnectPoint two,
+                           TrafficSelector selector,
+                           TrafficTreatment treatment,
+                           List<Constraint> constraints) {
+        this(appId, null, one, two, selector, treatment, constraints);
+    }
+    /**
+     * Creates a new host-to-host intent with the supplied host pair.
+     *
+     * @param appId       application identifier
+     * @param key       intent key
+     * @param one         first host
+     * @param two         second host
+     * @param selector    action
+     * @param treatment   ingress port
+     * @param constraints optional prioritized list of path selection constraints
+     * @throws NullPointerException if {@code one} or {@code two} is null.
+     */
+    public TwoWayP2PIntent(ApplicationId appId, Key key,
+                           ConnectPoint one, ConnectPoint two,
+                           TrafficSelector selector,
+                           TrafficTreatment treatment,
+                           List<Constraint> constraints) {
+        super(appId, key, Collections.emptyList(), selector, treatment, constraints);
+
+        // TODO: consider whether the case one and two are same is allowed
+        this.one = checkNotNull(one);
+        this.two = checkNotNull(two);
+
+    }
+
+    private static HostId min(HostId one, HostId two) {
+        return one.hashCode() < two.hashCode() ? one : two;
+    }
+
+    private static HostId max(HostId one, HostId two) {
+        return one.hashCode() >= two.hashCode() ? one : two;
+    }
+
+    /**
+     * Returns identifier of the first host.
+     *
+     * @return first host identifier
+     */
+    public ConnectPoint one() {
+        return one;
+    }
+
+    /**
+     * Returns identifier of the second host.
+     *
+     * @return second host identifier
+     */
+    public ConnectPoint two() {
+        return two;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id())
+                .add("key", key())
+                .add("appId", appId())
+                .add("resources", resources())
+                .add("selector", selector())
+                .add("treatment", treatment())
+                .add("constraints", constraints())
+                .add("one", one)
+                .add("two", two)
+                .toString();
+    }
+
+}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/TwoWayP2PIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/TwoWayP2PIntentCompiler.java
new file mode 100644
index 0000000..03e65f5
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/TwoWayP2PIntentCompiler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.net.intent.impl;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.TwoWayP2PIntent;
+import org.onosproject.net.resource.LinkResourceAllocations;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A intent compiler for {@link org.onosproject.net.intent.TwoWayP2PIntent}.
+ */
+@Component(immediate = true)
+public class TwoWayP2PIntentCompiler
+        extends ConnectivityIntentCompiler<TwoWayP2PIntent> {
+
+    @Activate
+    public void activate() {
+        intentManager.registerCompiler(TwoWayP2PIntent.class, this);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        intentManager.unregisterCompiler(TwoWayP2PIntent.class);
+    }
+
+    @Override
+    public List<Intent> compile(TwoWayP2PIntent intent, List<Intent> installable,
+                                Set<LinkResourceAllocations> resources) {
+        return Lists.newArrayList(
+                new PointToPointIntent(intent.appId(), intent.key(),
+                                       intent.selector(), intent.treatment(),
+                                       intent.one(), intent.two(),
+                                       intent.constraints()),
+                new PointToPointIntent(intent.appId(), intent.key(),
+                                       intent.selector(), intent.treatment(),
+                                       intent.two(), intent.one(),
+                                       intent.constraints()));
+
+    }
+}