1 | /** |
---|
2 | * Copyright (C) 2007 Funambol |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or modify |
---|
5 | * it under the terms of the Honest Public License. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * Honest Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the Honest Public License |
---|
13 | * along with this program; if not, write to Funambol, |
---|
14 | * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA |
---|
15 | */ |
---|
16 | package br.com.prognus.psync.synclet; |
---|
17 | |
---|
18 | import java.util.ArrayList; |
---|
19 | import java.util.HashMap; |
---|
20 | import java.util.List; |
---|
21 | import java.util.Map; |
---|
22 | |
---|
23 | import com.funambol.framework.core.AbstractCommand; |
---|
24 | import com.funambol.framework.core.Add; |
---|
25 | import com.funambol.framework.core.Item; |
---|
26 | import com.funambol.framework.core.ItemizedCommand; |
---|
27 | import com.funambol.framework.core.Meta; |
---|
28 | import com.funambol.framework.core.Replace; |
---|
29 | import com.funambol.framework.core.Sync; |
---|
30 | import com.funambol.framework.core.Sync4jException; |
---|
31 | import com.funambol.framework.core.SyncML; |
---|
32 | import com.funambol.framework.engine.pipeline.MessageProcessingContext; |
---|
33 | import com.funambol.framework.logging.FunambolLogger; |
---|
34 | import com.funambol.framework.logging.FunambolLoggerFactory; |
---|
35 | |
---|
36 | /** |
---|
37 | * This class extracts the PIM Items from the SyncML message. |
---|
38 | * It creates a Map with three lists that contain the items separated based on |
---|
39 | * their type. In case of large object, this class is able to recognize the |
---|
40 | * large object item and is able to handle it using a Map cached in the |
---|
41 | * MessageProcessingContext. |
---|
42 | * |
---|
43 | * @version $Id: PIMItemsHandler.java,v 1.1 2007-02-02 10:33:13 luigiafassina Exp $ |
---|
44 | */ |
---|
45 | public class PIMItemsHandler { |
---|
46 | |
---|
47 | // --------------------------------------------------------------- Constants |
---|
48 | private final FunambolLogger logger = |
---|
49 | FunambolLoggerFactory.getLogger("engine.pipeline"); |
---|
50 | |
---|
51 | private static final String BEGIN_VCARD = "BEGIN:VCARD" ; |
---|
52 | private static final String END_VCARD_RN = "END:VCARD\r\n" ; |
---|
53 | private static final String END_VCARD_N = "END:VCARD\n" ; |
---|
54 | private static final String BEGIN_VCALENDAR = "BEGIN:VCALENDAR" ; |
---|
55 | private static final String END_VEVENT_RN = |
---|
56 | "END:VEVENT\r\nEND:VCALENDAR\r\n"; |
---|
57 | private static final String END_VEVENT_N = "END:VEVENT\nEND:VCALENDAR\n"; |
---|
58 | private static final String END_VTODO_RN = |
---|
59 | "END:VTODO\r\nEND:VCALENDAR\r\n" ; |
---|
60 | private static final String END_VTODO_N = "END:VTODO\nEND:VCALENDAR\n" ; |
---|
61 | |
---|
62 | public static final String KEY_VCARD = "VCARD" ; |
---|
63 | public static final String KEY_VEVENT = "VEVENT"; |
---|
64 | public static final String KEY_VTODO = "VTODO" ; |
---|
65 | |
---|
66 | // ---------------------------------------------------------- Public methods |
---|
67 | |
---|
68 | /** |
---|
69 | * Processes input message to extract the items in order to separate them |
---|
70 | * into lists based on their type (vcard, vevent, vtodo). |
---|
71 | * |
---|
72 | * @param pContext the message processing context |
---|
73 | * @param message the message to be processed |
---|
74 | * |
---|
75 | * @return mapItems the map with the three list of items |
---|
76 | * @throws Sync4jException in case of errors |
---|
77 | */ |
---|
78 | public Map extractIncomingPIMItems(MessageProcessingContext pContext, |
---|
79 | SyncML message) |
---|
80 | throws Sync4jException { |
---|
81 | |
---|
82 | if (logger.isTraceEnabled()) { |
---|
83 | logger.trace("Starting creation of incoming items lists..."); |
---|
84 | } |
---|
85 | |
---|
86 | // |
---|
87 | // Caches the map with lists of items into request context: in this way, |
---|
88 | // if more synclet act on the same items, is not need to recycle on |
---|
89 | // items of the message |
---|
90 | // |
---|
91 | |
---|
92 | // |
---|
93 | // Contains the lists of the items |
---|
94 | // |
---|
95 | Map mapItems = null; |
---|
96 | mapItems = (Map)pContext.getRequestProperty("MAP_INCOMING_ITEMS"); |
---|
97 | |
---|
98 | if (mapItems != null) { |
---|
99 | if (logger.isTraceEnabled()) { |
---|
100 | logger.trace("Exists a map of items: is not need to cycle on message"); |
---|
101 | } |
---|
102 | |
---|
103 | // |
---|
104 | // In this case the first synclet has just created the map and so |
---|
105 | // the next synclets must act on that without recycle in message. |
---|
106 | // |
---|
107 | return mapItems; |
---|
108 | } else { |
---|
109 | |
---|
110 | if (logger.isTraceEnabled()) { |
---|
111 | logger.trace("Creates a map to contains the lists of items"); |
---|
112 | } |
---|
113 | mapItems = new HashMap(); |
---|
114 | initializeMapItems(mapItems); |
---|
115 | } |
---|
116 | |
---|
117 | // |
---|
118 | // Used to handle large object item |
---|
119 | // |
---|
120 | Item firstItem = null; |
---|
121 | Item lastItem = null; |
---|
122 | // |
---|
123 | // Cache the large object |
---|
124 | // |
---|
125 | Map cache = null; |
---|
126 | cache = (Map)pContext.getSessionProperty("LARGE_OBJ_INCOMING"); |
---|
127 | |
---|
128 | String syncURI = null; |
---|
129 | String itemLocURI = null; |
---|
130 | |
---|
131 | List<AbstractCommand> cmds = message.getSyncBody().getCommands(); |
---|
132 | for (AbstractCommand bodyc : cmds) { |
---|
133 | |
---|
134 | if (bodyc instanceof Sync) { |
---|
135 | syncURI = ((Sync)bodyc).getTarget().getLocURI(); |
---|
136 | |
---|
137 | // |
---|
138 | // Processes incoming commands to identifier and separate the items. |
---|
139 | // |
---|
140 | List<ItemizedCommand> syncCmds = ((Sync)bodyc).getCommands(); |
---|
141 | for (ItemizedCommand c : syncCmds) { |
---|
142 | |
---|
143 | // |
---|
144 | // Skip other commands than Add and Replace |
---|
145 | // |
---|
146 | if (!(Replace.COMMAND_NAME.equals(c.getName()) || |
---|
147 | Add.COMMAND_NAME.equals(c.getName())) ) { |
---|
148 | continue; |
---|
149 | } |
---|
150 | |
---|
151 | List<Item> items = c.getItems(); |
---|
152 | for (Item item: items) { |
---|
153 | |
---|
154 | itemLocURI = ((item.getSource() != null) |
---|
155 | ? item.getSource().getLocURI() |
---|
156 | : item.getTarget().getLocURI()); |
---|
157 | |
---|
158 | if (firstItem == null) { |
---|
159 | firstItem = item; |
---|
160 | } |
---|
161 | |
---|
162 | if (firstItem == item) { |
---|
163 | if (cache == null || cache.isEmpty()) { |
---|
164 | if (item.isMoreData()) { |
---|
165 | if (logger.isTraceEnabled()) { |
---|
166 | logger.trace("The item " + itemLocURI + |
---|
167 | " is a large object"); |
---|
168 | } |
---|
169 | cache = handleCache(cache, item, syncURI); |
---|
170 | } |
---|
171 | } else { |
---|
172 | cache = handleCache(cache, item, syncURI); |
---|
173 | } |
---|
174 | } //end if firstItem == item |
---|
175 | |
---|
176 | lastItem = item; |
---|
177 | |
---|
178 | if (!item.isMoreData()) { |
---|
179 | mapItems = fillMapItems(mapItems, item); |
---|
180 | } |
---|
181 | |
---|
182 | } //end for items |
---|
183 | } //end for cmds |
---|
184 | } //end if Sync |
---|
185 | |
---|
186 | if (lastItem != firstItem) { |
---|
187 | if (lastItem.isMoreData()) { |
---|
188 | cache = handleCache(cache, lastItem, syncURI); |
---|
189 | } |
---|
190 | } |
---|
191 | } //end if cmds |
---|
192 | |
---|
193 | pContext.setSessionProperty("LARGE_OBJ_INCOMING", cache); |
---|
194 | pContext.setRequestProperty("MAP_INCOMING_ITEMS", mapItems); |
---|
195 | |
---|
196 | return mapItems; |
---|
197 | } |
---|
198 | |
---|
199 | /** |
---|
200 | * Processes output message to extract the items in order to separate them |
---|
201 | * into lists based on their type (vcard, vevent, vtodo). |
---|
202 | * |
---|
203 | * @param message the message to be processed |
---|
204 | * |
---|
205 | * @return mapItems the map with the three list of items |
---|
206 | * @throws Sync4jException in case of errors |
---|
207 | */ |
---|
208 | public Map extractOutgoingPIMItems(SyncML message) |
---|
209 | throws Sync4jException { |
---|
210 | if (logger.isTraceEnabled()) { |
---|
211 | logger.trace("Starting creation of outgoing items lists..."); |
---|
212 | } |
---|
213 | |
---|
214 | Map mapItems = new HashMap(); |
---|
215 | initializeMapItems(mapItems); |
---|
216 | |
---|
217 | List<AbstractCommand> cmds = message.getSyncBody().getCommands(); |
---|
218 | for (AbstractCommand bodyc : cmds) { |
---|
219 | |
---|
220 | if (bodyc instanceof Sync) { |
---|
221 | |
---|
222 | // |
---|
223 | // Processes incoming commands to identifier and separate the items. |
---|
224 | // Note: the large object items will not be considered. |
---|
225 | // |
---|
226 | List<ItemizedCommand> syncCmds = ((Sync)bodyc).getCommands(); |
---|
227 | for (ItemizedCommand c : syncCmds) { |
---|
228 | |
---|
229 | // |
---|
230 | // Skip other commands than Add and Replace |
---|
231 | // |
---|
232 | if (!(Replace.COMMAND_NAME.equals(c.getName()) || |
---|
233 | Add.COMMAND_NAME.equals(c.getName())) ) { |
---|
234 | continue; |
---|
235 | } |
---|
236 | |
---|
237 | List<Item> items = c.getItems(); |
---|
238 | for (Item item: items) { |
---|
239 | mapItems = fillMapItems(mapItems, item); |
---|
240 | }//end for i items |
---|
241 | }//end for c commands |
---|
242 | }//end if Sync |
---|
243 | } |
---|
244 | |
---|
245 | return mapItems; |
---|
246 | } |
---|
247 | |
---|
248 | /** |
---|
249 | * If the item has the size set, probably it is a large object item. In this |
---|
250 | * case, after the synclet modifications, is need to fix the size property |
---|
251 | * with the real dimension of the data's item. |
---|
252 | * |
---|
253 | * @param i the item to fix the size property |
---|
254 | */ |
---|
255 | public void fixLargeObjectSize(Item i) { |
---|
256 | |
---|
257 | Meta m = i.getMeta(); |
---|
258 | if (m == null) { |
---|
259 | return; |
---|
260 | } |
---|
261 | if (m.getSize() != null && m.getSize() != 0) { |
---|
262 | int size = i.getData().getData().length(); |
---|
263 | if (logger.isTraceEnabled()) { |
---|
264 | logger.trace("Fixed size of large object item from " + |
---|
265 | m.getSize() + " to " + size); |
---|
266 | } |
---|
267 | i.getMeta().setSize(new Long(size)); |
---|
268 | } |
---|
269 | } |
---|
270 | |
---|
271 | // --------------------------------------------------------- Private methods |
---|
272 | |
---|
273 | /** |
---|
274 | * Handles large object storing its information into a Map. |
---|
275 | * |
---|
276 | * @param cache The map in which store the large object informations |
---|
277 | * @param item the large object |
---|
278 | * @param source the source with LocURI of item |
---|
279 | * |
---|
280 | * @return the map in which the large object is cached |
---|
281 | */ |
---|
282 | private Map handleCache(Map cache, Item item, String source) { |
---|
283 | |
---|
284 | if (cache == null || cache.isEmpty()) { |
---|
285 | // |
---|
286 | // It is the first chunk of the large object item |
---|
287 | // |
---|
288 | String uri = (item.getSource() != null) |
---|
289 | ? item.getSource().getLocURI() |
---|
290 | : item.getTarget().getLocURI() |
---|
291 | ; |
---|
292 | cache = new HashMap(); |
---|
293 | cache.put("itemLocURI", uri); |
---|
294 | cache.put("syncLocURI", source); |
---|
295 | cache.put("data", item.getData().getData()); |
---|
296 | |
---|
297 | } else { |
---|
298 | |
---|
299 | String cacheData = (String)cache.get("data"); |
---|
300 | String cacheLocURI = (String)cache.get("syncLocURI") |
---|
301 | + "/" |
---|
302 | + (String)cache.get("itemLocURI") |
---|
303 | ; |
---|
304 | |
---|
305 | String itemLocURI = source + "/" + ((item.getSource() != null) |
---|
306 | ? item.getSource().getLocURI() |
---|
307 | : item.getTarget().getLocURI()); |
---|
308 | |
---|
309 | if (cacheLocURI.equals(itemLocURI)) { |
---|
310 | cacheData = cacheData + item.getData().getData(); |
---|
311 | |
---|
312 | if (item.isMoreData()) { |
---|
313 | cache.put("data", cacheData); |
---|
314 | } else { |
---|
315 | item.getData().setData(cacheData); |
---|
316 | if (item.getMeta() == null) { |
---|
317 | item.setMeta(new Meta()); |
---|
318 | } |
---|
319 | item.getMeta().setSize(new Long(cacheData.length())); |
---|
320 | cache.clear(); |
---|
321 | } |
---|
322 | } else { |
---|
323 | cache.clear(); |
---|
324 | } |
---|
325 | } |
---|
326 | |
---|
327 | return cache; |
---|
328 | } |
---|
329 | |
---|
330 | /** |
---|
331 | * Fills the maps of items separated based on their type (vcard, vevent, |
---|
332 | * vtodo). |
---|
333 | * |
---|
334 | * @param mapItems the map in which add the list of items separated based on |
---|
335 | * them type |
---|
336 | * @param item the item to handle |
---|
337 | * |
---|
338 | * @return the map with lists of items |
---|
339 | */ |
---|
340 | private Map fillMapItems(Map mapItems, Item item) { |
---|
341 | |
---|
342 | List vcardItems = (List)mapItems.get(KEY_VCARD ); |
---|
343 | List veventItems = (List)mapItems.get(KEY_VEVENT); |
---|
344 | List vtodoItems = (List)mapItems.get(KEY_VTODO ); |
---|
345 | |
---|
346 | String data = item.getData().getData(); |
---|
347 | |
---|
348 | if ( data.startsWith(BEGIN_VCARD) && |
---|
349 | (data.endsWith(END_VCARD_RN) || |
---|
350 | data.endsWith(END_VCARD_N) )) { |
---|
351 | |
---|
352 | vcardItems.add(item); |
---|
353 | } else if (data.startsWith(BEGIN_VCALENDAR)) { |
---|
354 | if (data.endsWith(END_VEVENT_RN) || |
---|
355 | data.endsWith(END_VEVENT_N) ) { |
---|
356 | |
---|
357 | veventItems.add(item); |
---|
358 | } else if (data.endsWith(END_VTODO_RN) || |
---|
359 | data.endsWith(END_VTODO_N) ) { |
---|
360 | |
---|
361 | vtodoItems.add(item); |
---|
362 | } |
---|
363 | } |
---|
364 | if (logger.isTraceEnabled()) { |
---|
365 | logger.trace("List vcard : " + vcardItems.toString() ); |
---|
366 | logger.trace("List vevent: " + veventItems.toString()); |
---|
367 | logger.trace("List vtodo : " + vtodoItems.toString() ); |
---|
368 | } |
---|
369 | return mapItems; |
---|
370 | } |
---|
371 | |
---|
372 | /** |
---|
373 | * Initializes the Map of items. |
---|
374 | * |
---|
375 | * @param mapItems the empty map |
---|
376 | */ |
---|
377 | private void initializeMapItems(Map mapItems) { |
---|
378 | |
---|
379 | List vcardItems = new ArrayList(); |
---|
380 | List veventItems = new ArrayList(); |
---|
381 | List vtodoItems = new ArrayList(); |
---|
382 | |
---|
383 | mapItems.put(KEY_VCARD , vcardItems ); |
---|
384 | mapItems.put(KEY_VEVENT, veventItems); |
---|
385 | mapItems.put(KEY_VTODO , vtodoItems ); |
---|
386 | } |
---|
387 | } |
---|